Day43-动态规划,leetcode300,674,718
- 最长递增子序列
- 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
- 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
- 思路
- 1.确定dp数组定义及下标的含义:dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度
- 2.确定递推公式:if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1)
- 3.dp数组如何初始化:每一个i,对应的dp[i](即最长递增子序列)起始大小至少都是1
- 4.确定遍历顺序:dp[i] 是有0到i-1各个位置的最长递增子序列 推导而来,那么遍历i一定是从前向后遍历。遍历i的循环在外层,遍历j则在内层
- 5.举例推导dp数组,打印dp数组:
/**
* 1. dp数组定义
dp[i] 表示:以 nums[i] 结尾的最长递增子序列的长度。
* 2. 初始化
每个位置的初始值都是1,因为每个元素自己单独也能成为一个长度为1的递增子序列。
* 3. 状态转移
外层循环 i 表示当前考察的结尾元素。
内层循环 j 遍历 i 之前的所有元素。
如果 nums[i] > nums[j],说明 nums[i] 可以接在 nums[j] 后面形成递增子序列。
更新 dp[i] 为 dp[j] + 1 的最大值。
* 4. 结果更新
result 记录所有 dp[i] 的最大值,即最长递增子序列的长度。
* 5. 返回结果
返回 result,即最长递增子序列的长度。
*/
const lengthOfLIS = (nums) => {
let dp = Array(nums.length).fill(1);
let result = 1;
for(let i = 1; i < nums.length; i++) {
for(let j = 0; j < i; j++) {
if(nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j]+1);
}
}
result = Math.max(result, dp[i]);
}
return result;
};
- 最长连续递增序列
- 给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
- 连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。
- 思路
- 不连续递增子序列的跟前0-i 个状态有关,连续递增的子序列只跟前一个状态有关
- 1.确定dp数组定义及下标的含义:dp[i]:以下标i为结尾的连续递增的子序列长度为dp[i]
- 2.确定递推公式:如果 nums[i] > nums[i - 1],那么以 i 为结尾的连续递增的子序列长度 一定等于 以i - 1为结尾的连续递增的子序列长度 + 1 。即:dp[i] = dp[i - 1] + 1
- 3.dp数组如何初始化:以下标i为结尾的连续递增的子序列长度最少也应该是1,即就是nums[i]这一个元素。所以dp[i]应该初始1;
- 4.确定遍历顺序:dp[i + 1]依赖dp[i],所以一定是从前向后遍历。
- 5.举例推导dp数组,打印dp数组:
// 动态规划
var findLengthOfLCIS = function(nums) {
let dp = Array(nums.length).fill(1)
let result = 1
for (let i = 1; i < nums.length; i++) {
if (nums[i] > nums[i-1]) {
dp[i] = Math.max(dp[i], dp[i-1]+1)
}
result = Math.max(result, dp[i])
}
return result
};
// 贪心
const findLengthOfLCIS = (nums) => {
if (nums.length === 0) return 0 // 如果数组为空,直接返回0。
let result = 1 // result:记录最长连续递增子序列的长度,初始为1。
let count = 1 // count:记录当前连续递增子序列的长度,初始为1。
/**
* 从第二个元素开始遍历。
* 如果当前元素大于前一个元素,说明递增,count++。
* 否则,递增断开,count重置为1。
* 每次更新result为当前最大连续递增长度。
*/
for (let i = 1; i < nums.length; i++) {
if (nums[i] > nums[i-1]) {
count++
} else {
count = 1
}
if (count > result) result = count
}
return result // 返回最长连续递增子序列的长度
}
- 最长重复子数组
- 给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。
- 思路
- 1.确定dp数组定义及下标的含义:dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]
- 2.确定递推公式:当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;
- 3.dp数组如何初始化:dp[i][0] 和dp[0][j]初始化为0
- 4.确定遍历顺序:外层for循环遍历A,内层for循环遍历B。
- 5.举例推导dp数组,打印dp数组:A: [1,2,3,2,1],B: [3,2,1,4,7]
// 二维
// dp[i][j] 表示:以 A[i-1] 和 B[j-1] 结尾的最长公共子数组的长度。
const findLength = (A, B) => {
// A、B数组的长度
const [m, n] = [A.length, B.length];
// dp数组初始化,都初始化为0
// 创建一个 (m+1) 行 (n+1) 列的二维数组,全部初始化为0。多加一行一列是为了处理边界,方便状态转移。
const dp = new Array(m + 1).fill(0).map(x => new Array(n + 1).fill(0));
// 初始化最大长度为0
let res = 0;
/**
* 外层循环遍历A,内层循环遍历B。
* 如果 A[i-1] === B[j-1],说明两个数组在当前位置有相同元素,可以在前一个公共子数组的基础上加1。
* 每次更新最大长度 res
*/
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
// 遇到A[i - 1] === B[j - 1],则更新dp数组
if (A[i - 1] === B[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
// 更新res
res = dp[i][j] > res ? dp[i][j] : res;
}
}
// 遍历完成,返回res,res 就是最长公共子数组的长度
return res;
};
参考&感谢各路大神
宝剑锋从磨砺出,梅花香自苦寒来。

浙公网安备 33010602011771号