dp:最长上升子序列:TreeSet.ceiling;暴力dp:二分法:用ArrayList写的二分法;arraylist中set和and的区别

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

TreeSet.ceiling

package TreeSet;

import java.util.Arrays;
import java.util.Scanner;
import java.util.TreeSet;

public class 最长上升子序列 {
	//利用更简单的API TreeSet的Ceiling方法,应该是logN??但是最坏情况下会退化的把
	//TreeSet.ceiling(x)方法可以直接找出set中大于x的最小数字,如果不存在则返回null。
	//1、如果这个数字存在,则删除这个数字,然后把x插入set中,相当于代替该数字。
	//2、如果这个数字不存在,说明x大于set中任何数字,直接把x插入set中。
	//最后返回set的大小即可。

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int[] a=new int[n];
		for(int i=0;i<n;i++)
			a[i]=sc.nextInt();
		
		TreeSet<Integer> set=new TreeSet<>();
		for(int i=0;i<a.length;i++){
			Integer c=set.ceiling(a[i]);
			//如果set中没有大于nums[i]的最小数字,那么就符合最长上升子序列,加入
			//如果有,把让那个移除那个数字,换做这个nums[i],因为nums[i]更小
			//其实这边先判断 c!=null代码会更为简洁一点
			if(c==null)
				set.add(a[i]);
			else{
				set.remove(c);
				set.add(a[i]);
			}
		}
		System.out.println(set);
		System.out.println(set.size());
	}

}


暴力DP

package dp;

import java.util.Scanner;

public class 最长上升子序列暴力dp {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int n1=sc.nextInt();
		int[] a=new int[n1];
		for(int i=0;i<n1;i++)
			a[i]=sc.nextInt();
		
		int n=a.length;
		//dp[i]代表到数组第i个位置的最长上升子序列
		int[] dp=new int[n+1];
		int res=1;
		//枚举第i个位置
		for(int i=1;i<=n;i++){
			dp[i]=1;
			//枚举它所有前面位置
			for(int j=1;j<i;j++){
				//如果第i个位置的数比前面大,符合最长上升子序列,更新
				if(a[j-1]<a[i-1]){
					dp[i]=Math.max(dp[i],dp[j]+1);
				}
				//记录全局最大值
	            res = Math.max(res, dp[i]);
			}
		}
		System.out.println(res);
	}

}

二分法

package dp;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class 最长上升子序列二分法 {
	//使用二分优化
	//优化:一旦前面有两个dp值一样了,比如dp[i] = dp[j],并缺nums[i] > nums[j] ,那就只要考虑第j个就可以了
	//启示:同样的dp值,存一个坐标,这个坐标对应的nums[index]值最小。
	//怎么做?对于每个dp值,保存对应的nums[i]的值
	//序列是单调上升的,在单调上升中找最后一个比自己小的数用二分法 lon2n,二分法很重要

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);		
		int n=sc.nextInt();
		int[] a=new int[n];
		for(int i=0;i<n;i++)
			a[i]=sc.nextInt();
		
		//System.out.println(Arrays.toString(a));
		
		int[] b=new int[n];//用来保存最长上升子序列的列表b
		int res=0;
		for(int i=0;i<n;i++){
			//binarySearch(Object[], int fromIndex, int toIndex, Object key)
			int idx=Arrays.binarySearch(b,0,res,a[i]);//在b中,从0到res的索引,不包括res,值a[i];
			//即二分查找第一个a[i],都比他大,且没有在数组之内,则返回-(toIndex)+1;即-res-1;,第一个数即插到最前面
			//System.out.println("idx="+idx);
			if(idx<0)//如果小于0,就说比范围内的数组都大,
				idx=-idx-1;//idx=最后一个元素的位置
			//把这个nums[i]放在插入的位置上
			//System.out.println("idx后="+idx);
			b[idx]=a[i];
			if(idx==res)
				res++;//如果相等res++;
		}
		//for(int ans:b)
			//System.out.print(ans+" ");
		//System.out.println();
		System.out.println(res);
		
	}

}

在这里插入图片描述

用ARRAYLIST写的

package dp;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class 最长递增子序列 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int[] a=new int[n];
		for(int i=0;i<n;i++)
			a[i]=sc.nextInt();
		
		//用来保存最长上升子序列的列表arr
		ArrayList<Integer> arr=new ArrayList<>();
		for(int i=0;i<n;i++){
			//如果nums[i]比arr的最大值还大,可以组成一个更长的子序列
			//并将其添加到arr末尾
			if(arr.size()==0||arr.get(arr.size()-1)<a[i]){
				arr.add(a[i]);
				continue;
			}
			 //如果nums[i]比arr最大值小,就要在arr中找查找一个合适的位置,
			//将nums[i]放入,这查找过程是二分查找
			int begin=0;
			int end=arr.size()-1;
			while(begin<=end){
				int mid=(begin+end)/2;
				if(arr.get(mid)>a[i])
					end=mid-1;
				else if(arr.get(mid)<a[i])
					begin=mid+1;
				else{
					begin=mid;
					break;
				}
			}
			arr.set(begin,a[i]);//替换掉
		}
		System.out.println(arr.size());
	}

}

发布了342 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44522477/article/details/105544622