持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
动态规划(Dynamic Programming)是一种分阶段求解决策问题的数学思想,它通过把原问题分解为简单的子问题来解决复杂问题。
最长递增子序列
给你一个整数数组 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
首先需要注意的是题目要求的是子序列
而不是子串
,子串
一定是连续的,而子序列
不一定是连续的。
暴力解法
先得到输入数组的所有子序列,然后遍历这些子序列判断是否严格上升,并且找出最长的哪一个,不过这种方法在穷举所有子序列的时候时间复杂度非常高。
动态规划
- 首先定义状态,明确
dp
数组的定义,即dp[i] 表示:以 nums[i] 结尾 的最长严格递增子序列的长度。 - 然后是找出状态转移方程,本题中,只有nums[i]大于它前面的数字,才能拼接在后面形成一个更长的上升子序列。
- 最后是base case,对于每个字符来说,它自己一个字符也属于上升子序列。所以dp[i] = 1。
代码如下:
int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
Arrays.fill(dp, 1);
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j])
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
int res = 0;
for (int i = 0; i < dp.length; i++) {
res = Math.max(res, dp[i]);
}
return res;
}
复杂度分析
- 时间复杂度:O(N^2)
- 空间复杂度:O(N),只是额外使用了一个dp数组。