题目地址(300. 最长递增子序列)

https://leetcode-cn.com/problems/longest-increasing-subsequence/

题目描述

  1. 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
  2. 子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
  3. 示例 1
  4. 输入:nums = [10,9,2,5,3,7,101,18]
  5. 输出:4
  6. 解释:最长递增子序列是 [2,3,7,101],因此长度为 4
  7. 示例 2
  8. 输入:nums = [0,1,0,3,2,3]
  9. 输出:4
  10. 示例 3
  11. 输入:nums = [7,7,7,7,7,7,7]
  12. 输出:1
  13. 提示:
  14. 1 <= nums.length <= 2500
  15. -104 <= nums[i] <= 104
  16. 进阶:
  17. 你可以设计时间复杂度为 O(n2) 的解决方案吗?
  18. 你能将算法的时间复杂度降低到 O(n log(n)) 吗?

前置知识


公司

  • 暂无

思路

我们的定义是这样的:dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度
base case:dp[i] 初始值为 1,因为以 nums[i] 结尾的最长递增子序列起码要包含它自己
要思考如何设计算法逻辑进行状态转移,才能正确运行呢?这里就可以使用数学归纳的思想:
假设我们已经知道了 dp[0..4] 的所有结果,我们如何通过这些已知结果推出 dp[5] 呢
image.png
根据刚才我们对 dp 数组的定义,现在想求 dp[5] 的值,也就是想求以 nums[5] 为结尾的最长递增子序列。
nums[5] = 3,既然是递增子序列,我们只要找到前面那些结尾比 3 小的子序列,然后把 3 接到最后,就可以形成一个新的递增子序列,而且这个新的子序列长度加一。
显然,可能形成很多种新的子序列,但是我们只选择最长的那一个,把最长子序列的长度作为 dp[5] 的值即可
总结一下如何找到动态规划的状态转移关系:
1、明确 dp 数组的定义。这一步对于任何动态规划问题都很重要,如果不得当或者不够清晰,会阻碍之后的步骤。
2、根据 dp 数组的定义,运用数学归纳法的思想,假设 dp[0…i-1] 都已知,想办法求出 dp[i],一旦这一步完成,整个题目基本就解决了。
但如果无法完成这一步,很可能就是 dp 数组的定义不够恰当,需要重新定义 dp 数组的含义;或者可能是 dp 数组存储的信息还不够,不足以推出下一步的答案,需要把 dp 数组扩大成二维数组甚至三维数组
gif2.gif

关键点


代码

  • 语言支持:Java

Java Code:

class Solution {
        public int lengthOfLIS(int[] nums) {
            //定义dp数组 下标i位置的最长递增子列是dp[i]
            int[] dp = new int[nums.length];
            //base case 将每个数组元素初始为1 因为最长递增子列最少为1
            Arrays.fill(dp, 1);
            int res = 0;
            //外层遍历的是整个nums数组 旨在整个数组的每一个数字 都求出他的最大增长子序列
            for (int i = 0; i < nums.length; i++) {
                //内层循环 循环的是当前外层锁定 想要求出在i之前比i在数组中的值要小的数 求出这个数就可以将i添加到之前求出的dp后面
                //也就是求出了新的最大增长子序列
                for (int j = 0; j < i; j++) {
                    if (nums[i] > nums[j]) {
                        dp[i] = Math.max(dp[i], dp[j] + 1);
                    }
                }
                //遍历整个dp数组 将其中最大的数字返回 这也就是我们想要的最大增长子序列
                res = Math.max(res, dp[i])
            }
            return res;
        }
    }

复杂度分析

令 n 为数组长度。

  • 时间复杂度:300. 最长递增子序列 - 图3
  • 空间复杂度:300. 最长递增子序列 - 图4#card=math&code=O%28n%29&id=T6dfl)