动态规划之LIS(最长上升子序列)

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)
类似地,我们可以使用二分查找改变“上确界”和“下确界”以及符号(“<”、“<=”、“>”、“>=”等)求出最长不下降、不上升、严格下降子序列问题。

posted @ 2020-10-16 14:32  小帆敲代码  阅读(153)  评论(0编辑  收藏  举报