https://www.cnblogs.com/frankchenfu/p/7107019.html
【题目描述】
给定N个数,求这N个数的最长上升子序列的长度。
【样例输入】
7
2 5 3 4 1 7 6
【样例输出】
4
什么是最长上升子序列? 就是给你一个序列,请你在其中求出一段不断严格上升的部分,它不一定要连续。
就像这样:2,3,4,7和2,3,4,6就是序列2 5 3 4 1 7 6的两种选取方案。最长的长度是4.
朴素算法:
使用dp[i]表示以第i个数值结尾的最长上升子序列的长度。
int lis(int[] nums){
if(nums==null||nums.length==0)
return 0;
int[]dp=new int[nums.length];
int re=1;
for(int i=0;i<nums.length;i++){
dp[i]=1;
for(int j=0;j<i;j++){
if(nums[j]<nums[i]){
if(dp[i]<dp[j]+1){
dp[i]=dp[j]+1;
}
}
if(re<dp[i])
re=dp[i];
}
return re;
}
时间复杂度:O(n^2)
空间复杂度:O(n)
二分算法
其实在之前有一种想法是模拟一个栈,当序列降序时,替换其中的数值。但是当时将这个想法略过去了。
而二分法的实现是首先当nums[i]>栈顶元素时,乖乖排在队尾(栈顶)。但是当num[i]<=栈顶元素时(即降序时)因为保证最长长度不变的情况下,当使用二分算法替换在栈中的元素时,栈中元素数量不变,但是可能导致栈顶元素变小(即后来的元素有新进来的机会,栈中整体数值下降)。
public static int lis(int[] nums) {
if(nums==null||nums.length==0)
return 0;
ArrayList<Integer> list=new ArrayList<Integer>();
list.add(nums[0]);
for(int i=1;i<nums.length;i++) {
if(nums[i]>list.get(list.size()-1))
list.add(nums[i]);
else {
int start=0,end=list.size()-1;
int mid=(start+end)/2;
while(start<=end) {
mid=(start+end)/2;
if(list.get(mid)<nums[i]) {
start=mid+1;
}else if(list.get(mid)==nums[i]) {
break;
}else {
end=mid-1;
}
}
if(start>end)
list.set(start, nums[i]);
}
}
return list.size();
}
时间复杂度:O(nlogn)
空间复杂度:O(n)
类似地,我们可以使用二分查找改变“上确界”和“下确界”以及符号(“<”、“<=”、“>”、“>=”等)求出最长不下降、不上升、严格下降子序列问题。