动态规划做题顺序:
- 确定 dp[i] 的意义
- 确定递推公式
- 初始化 dp[i]
- 确定遍历顺序
- 举例分析
300. 最长递增子序列
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
链接:力扣
解题思路:
本题主要考虑 元素的递增,因此确定 递推公式 时,也要考虑 当前遍历后的子序列的尾元素 和 即将要遍历的元素 谁大,从而确定 递归序列的尾元素 是谁。
所以 dp[i] 就是表示 当前遍历后的子序列的长度。并且可以确定递推公式:
dp[i] = Math.max(dp[i], dp[j] + 1) && nums[i] > nums[j]
这里的比较是针对取最大的 dp[j] 的子序列长度,不是将 dp[i] 和 dp[j] 进行比较,也可以理解成这样:
dp[i] = Math.max(dp[j]) + 1 && nums[i] > nums[j] && 0 <= j < i
再求解 Math.max(dp[i]) 即可
初始化长度为1,因为长度至少是1。
var lengthOfLIS = function(nums) {
if(nums.length == 1) return 1
// 初始化都为1
let dp = Array(nums.length).fill(1)
// 递增子序列的长度
let res = 1
for(let i = 1; i < nums.length; i++) {
for(let j = 0; j < i; j++) {
// 获取最大的 dp[j]
if(nums[i] > nums[j]) dp[i] = Math.max(dp[i], dp[j] + 1)
}
// 得到最长子序列长度
res = Math.max(dp[i], res)
}
return res
}
674. 最长连续递增序列
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列
链接:力扣
解题思路:
本题与上一题的不同之处在于,这道题强调 “连续”,也就是说不比较 nums[j] 与 nums[i](j 从0遍历到 i),而是比较 nums[i] 与 nums[i - 1],也就无需双层循环。连续递增的子序列长度最小是1,所以初始化是1。递推公式是:
dp[i] = dp[i - 1] + 1 && nums[i] > nums[i - 1]
var findLengthOfLCIS = function(nums) {
if(nums.length == 1) return 1
// 初始化都为1
let dp = Array(nums.length).fill(1)
// 递增子序列的长度
let res = 1
for(var i = 1; i < nums.length; i++) {
if(nums[i] > nums[i-1]) dp[i] = dp[i-1] + 1
if (dp[i] > res) res = dp[i]
}
return res
}
给两个整数数组 nums1
和 nums2
,返回 两个数组中 公共的 、长度最长的子数组的长度 。
链接:力扣
解题思路:
本题是要在连续最长子序列基础上,找到两个数组中的公共序列,因此,确定 dp[i][j] 是以 i - 1结尾的子序列1,和以 j - 1结尾的子序列2,最长重复子序列长度为 dp[i][j]。确定递推公式是:
dp[i][j] = dp[i - 1][j - 1] + 1 && nums1[i - 1] == nums2[j - 1]
关于初始化:dp[i][0] 和dp[0][j] 在整个递推过程中没有实际意义,所以dp[i][0] 和dp[0][j]初始化为0。(这里需要初始化,方便递推)这里的遍历顺序可以先1后2或者先2后1都可以。
var findLength = function(nums1, nums2) {
const len1 = nums1.length, len2 = nums2.length
// dp数组初始化,都初始化为0
const dp = Array.from({ length: len1 + 1 }, () => Array(len2 + 1).fill(0))
// 初始化最大长度为0
let res = 0
for(let i = 1; i <= len1; i++) {
for(let j = 1; j <= len2; j++) {
if(nums1[i - 1] == nums2[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1
res = Math.max(res, dp[i][j])
}
}
return res
}