IDEAS SPACE

一步步提升技术 做出你想做的事
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

最长递增子序列

Posted on 2024-10-24 23:55  LKB_HUGH  阅读(29)  评论(0)    收藏  举报

定义

代码实现

动态规划实现(dp)

点击查看代码
class Solution {
	public int lengthOfLIS(int[] nums) {
		int len = 0;
		// dp的定义是,0-i位置最长递增子序列的长度
		int[] dp = new int[nums.length];
		for(int i = 0; i < nums.length; i++) {
			// 自己的长度,1
			dp[i] = 1;
			// 遍历之前的所有数,找到一个比自己小的数j,加上0-j的最长子序列。
			for(int j = 0; j < i; j++) {
				if(nums[i] > nums[j]) {
					dp[i] = Math.max(dp[i], dp[j] + 1);
				}
			}
            len = Math.max(len, dp[i]);
		}
		return len;
	}
}

记录0-i之间的严格递增的序列

为什么使用这个序列,我们从dp实现可以看到,我们第二层for循环的主要目的是找到,小于num[i]的长度记录。那么我们就可以使用二分搜索的方式找到小于num[i]的数。

那么如何生成这个数组呢?

那么假设,我们有一个数组end[],它内部是单调递增的数组。
  • 查询一个数,查一个小于等于它的数,就能得到相应的效果【第二层for循环的目的】
  • 更新数呢,我们要保证严格的递增,假设我们找到小于等于它的数。那么我应该更新吗?
    • 由于我们希望得到的长度尽量长,所以,这里的单调递增数组里面的数尽量小是最好的。
      原因如下:
      【1,3,2,5,3】我们的递增数组会有几个阶段【1,3】-> 【1,2】-> 【1,2,5】-> 【1,2,3】在这个情况下,在第一阶段变成第二阶段的时候,2不替换3,那么到最后阶段的3的时候并不会进入这个数组中。
      在这里2进来的时候,找到的是1,再下标+1,替换掉3。
      而在5的时候,找到的是2,再下标+1,添加一个新的数。(这里有一个难以实现的事情,怎么判断是找到一个绝对大的数,还是一个等于的数,在最后面的数组中)
      那么,现在,我们换一种思考方式,如果找的是大于等于查找数的最小值(即最左边的值),如果找到那就直接替换,保证整体最小,如果找不到那么现在这个数最大,直接添加一个新的数。
点击查看代码
    public int bs(int[] end, int len, int target) {
        int l = 0, r = len - 1;
        int ans = -1;
        while(l <= r) {
            int m = l + ((r - l) >> 1);
            if(end[m] >= target) {
                r = m - 1;
                ans = m;
            } else {
                l = m + 1;
            }
        }
        return ans;
    }