【Leetcode】300. 最长递增子序列

题目描述

在这里插入图片描述

// 300. 最长递增子序列

// 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

// 子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素
// 的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

题解

// 动态规划
// 定义动态规划数组dp,定义答案保存数res,我们贪心地把最大值更新给res。
//
// 解释1
// dp[i]是遍历到 i 位置时能够取得的最长严格递增序列长度,那dp[i]肯定是上
// 个dp最长序列状态 + 1,假设上一个dp最长子序列状态是dp[j],所以有dp[i] = dp[j] + 1,
// 那么首先j小于i,而且dp[j]本身肯定也是i位置以前的最长子序列,怎么取dp[j]?
// 直接for循环遍历0到i的位置( for (int j = 0; j < i; j++) ),取这些dp[j]的最大值,
// 记为tempMax, 这个tempMax + 1 = dp[i],得到dp[i]之后贪心地把res和dp[i]的最大值
// 更新给res。最后返回res即可。
// 
// 解释2
// 最朴素的情况,dp[i]表示遍历到i位置时,最长递增子序列的长度,
// 那么dp[i]实际上就应该等于上一个位置最长递增子序列长度dp[i-1],再+1,
// 这个dp[i-1]可以一直回推到最初的基础情况,基础情况是什么呢?
// 首先每个单独的数字本身就可以作为最长递增子序列,长度为1,所以dp[0]=1。
// 然后如果某个数字的左边,出现了一个比自身小的数字,那么它的最长递增子
// 序列长度就要+1。
// 
// 因此我们for循环遍历nums和dp的所有位置,遍历元素即为nums[i],对应的
// 最长递增子序列的长度即为dp[i],第二层for循环从头遍历到i位置,索引为j,
// 用于查找i左边的元素nums[j]是否存在比nums[i]小的情况,如果存在的话,
// 取从[0, i]区间内所有位置上最大的最长递增子序列的长度dp[j](这里
// 也是贪心),将[0, i]区间上最大的dp[j]记录给变量tempMax,i左边的j遍历
// 完之后,将tempMax(即i左边最大的dp[j])加1赋给当前dp[i],
// 然后将res和dp[i]中的最大值始终更新res。最后返回res即可。
// 
// 执行用时:64 ms, 在所有 Java 提交中击败了78.40%的用户
// 内存消耗:37.8 MB, 在所有 Java 提交中击败了93.54%的用户
class Solution {
    public int lengthOfLIS(int[] nums) {
		if (nums.length == 0)
			return 0;
		int[] dp = new int[nums.length];
		dp[0] = 1;
		int res = 1;
		for (int i = 1; i < nums.length; i++) {
			int tempMax = 0;
			for (int j = 0; j < i; j++) {
				if (nums[j] < nums[i]) {
					tempMax = Math.max(tempMax, dp[j]);
				}
			}
			dp[i] = tempMax + 1;
			res = Math.max(res, dp[i]);
		}
		return res;
    }
}


// 动态规划 + 二分查找
// 注意:这个方法通过贪心地找数值上尽可能小的最长递增子序列,求出序列长。
// 本方法比单纯的动态规划更优的前提是,题目只要求我们求长度,而不要我们
// 给出具体的最长递增子序列。
// 具体解释可以看看 https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/zui-quan-zui-chang-shang-sheng-zi-xu-lie-lei-wen-t/
// 
// 创建longestSubList数组,用于保存最长递增子序列,定义len为数组的长度。
// 对nums的元素进行遍历nums,每个元素都二分查找插入longestSubList中,
// 我们认为,选取的最长递增子序列中的元素,尽可能的小,是对取得最长递增
// 子序列帮助最大的。根据binarySearch函数性质,如果num在longestSubList
// 中没有出现过,二分查找插入索引i为负数,那么存在:
// 1.若num比longestSubList中所有元素都小,则直接插入longestSubList第一位中
// (可能覆盖掉原来第一位元素)。
// 2.若num比longestSubList中所有元素都大,则直接插入longestSubList末位。
// 3.若num应该属于longestSubList的中间某个位置,则直接插入该位置(可能
// 覆盖掉某个元素)。
// 每次插入元素num之后,更新一下len,插入过程中i必定会出现指向数组的尾部
// 的情况,数组尾部索引+1就等于当前数组的长度,所以每次拿i+1和len中更大
// 的值去更新len即可。
// 
// 二分搜索函数Arrays.binarySearch函数:
// 如果num在数组中,直接返回num应该插入的索引,索引从0开始计数。
// 首先num如果不在数组中,返回的索引会加个负号,而且索引是从1开始计数的。
// 如果搜索值num大于数组所有值,则返回-(length + 1),
// 如果搜索值num小于数组所有值,则返回-1,
// 以[1, 3, 4, 5]为例,如果搜索num=2的位置,那么2应该排数组第二名位置,
// 所以函数返回-2。如果搜索num=3的位置,则会直接返回1。
// 所以如果搜索的num在数组不存在,返回的索引我们需要用,需要转化一下,
// 把数值的负号消掉,再-1。
// 
// 打印中间过程有:
// [10,9,2,5,3,7,101,18]
// 
// [0, 0, 0, 0, 0, 0, 0, 0]
// [10, 0, 0, 0, 0, 0, 0, 0]
// [9, 0, 0, 0, 0, 0, 0, 0]
// [2, 0, 0, 0, 0, 0, 0, 0]
// [2, 5, 0, 0, 0, 0, 0, 0]
// [2, 3, 0, 0, 0, 0, 0, 0]
// [2, 3, 7, 0, 0, 0, 0, 0]
// [2, 3, 7, 101, 0, 0, 0, 0]
// [2, 3, 7, 18, 0, 0, 0, 0]

// 执行用时:3 ms, 在所有 Java 提交中击败了94.50%的用户
// 内存消耗:38.1 MB, 在所有 Java 提交中击败了62.40%的用户
class Solution {
    public int lengthOfLIS(int[] nums) {
		if (nums.length == 0)
			return 0;
		int[] longestSubList = new int[nums.length];
		int len = 0;
		for (int num: nums) {
			int i = Arrays.binarySearch(longestSubList, 0, len, num);
			if (i < 0)
				i = -i - 1;
			longestSubList[i] = num;
			len = Math.max(i + 1, len);
			// arrPrint(arr);
		}
		return len;
    }
	
	// public void arrPrint(int[] arr) {
        // StringBuilder sb = new StringBuilder();
        // sb.append("[");
        // for (int num: arr) {
            // sb.append(num + ", ");
        // }
        // sb.delete(sb.length() - 2, sb.length());
        // sb.append("]");
        // System.out.println(sb.toString());
    // }
}


// 优化一下
// 如果插入num元素之后,i出现了与插入前的len相等的情况,说明i是在数组尾部
// 插入的,没有覆盖掉任何值,还使得数组长度增加一位。这时候要更新一下len值,
// 将len累加一次,这样就满足数组尾部索引+1等于数组长度len这个定律了。
// 
// 执行用时:2 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:37.9 MB, 在所有 Java 提交中击败了85.37%的用户
class Solution {
    public int lengthOfLIS(int[] nums) {
		if (nums.length == 0)
			return 0;
		int[] longestSubList = new int[nums.length];
		int len = 0;
		for (int num: nums) {
			int i = Arrays.binarySearch(longestSubList, 0, len, num);
			if (i < 0)
				i = -i - 1;
			longestSubList[i] = num;
			if (i == len) {
				len++;
			}
		}
		return len;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

锥栗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值