持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情
动态规划(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[i] 的值代表
nums
以 nums[i] 结尾的最长子序列长度。 - 如果一个较大的数接在较小的数后面,就会形成一个更长的子序列。只要 nums[i] 严格大于在它位置之前的某个数,那么 nums[i] 就可以接在这个数后面形成一个更长的上升子序列。
3.转移方程为
dp[i] = max(dp[i], dp[j] + 1) for j in [0, i)
。 dp[i] = 1
,1 个字符是长度为 1 的上升子序列。- 不能返回最后一个状态值,最后一个状态值只表示以 nums[len - 1] 结尾的上升子序列的长度,状态数组 dp 的最大值才是题目要求的结果。
代码如下:
fun lengthOfLIS(nums: IntArray): Int {
if (nums.size == 0) return 0
val dp = IntArray(nums.size)
var res = 0
Arrays.fill(dp, 1)
for (i in nums.indices) {
for (j in 0 until i) {
if (nums[j] < nums[i]) dp[i] = Math.max(dp[i], dp[j] + 1)
}
res = Math.max(res, dp[i])
}
return res
}
复杂度分析:
- 时间复杂度 O(N^2) ,遍历计算 dp 列表需 O(N)),计算每个 dp[i] 需 O(N)。
- 空间复杂度 O(N) ,dp 列表占用线性大小额外空间。