给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [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());
}
}