题目地址(300. 最长递增子序列)
https://leetcode-cn.com/problems/longest-increasing-subsequence/
题目描述
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。示例 1:输入:nums = [10,9,2,5,3,7,101,18]输出:4解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。示例 2:输入:nums = [0,1,0,3,2,3]输出:4示例 3:输入:nums = [7,7,7,7,7,7,7]输出:1提示:1 <= nums.length <= 2500-104 <= nums[i] <= 104进阶:你可以设计时间复杂度为 O(n2) 的解决方案吗?你能将算法的时间复杂度降低到 O(n log(n)) 吗?
前置知识
公司
- 暂无
思路
我们的定义是这样的:dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度。
base case:dp[i] 初始值为 1,因为以 nums[i] 结尾的最长递增子序列起码要包含它自己
要思考如何设计算法逻辑进行状态转移,才能正确运行呢?这里就可以使用数学归纳的思想:
假设我们已经知道了 dp[0..4] 的所有结果,我们如何通过这些已知结果推出 dp[5] 呢?
根据刚才我们对 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 数组扩大成二维数组甚至三维数组
关键点
代码
- 语言支持: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 为数组长度。
- 时间复杂度:
- 空间复杂度:
#card=math&code=O%28n%29&id=T6dfl)
