Day43-动态规划,leetcode300,674,718

  1. 最长递增子序列
  • 给你一个整数数组 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;
};


  1. 最长连续递增序列
  • 给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
  • 连续递增的子序列 可以由两个下标 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 // 返回最长连续递增子序列的长度
}


  1. 最长重复子数组
  • 给两个整数数组 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;
};



参考&感谢各路大神

posted @ 2025-07-09 09:16  安静的嘶吼  阅读(5)  评论(0)    收藏  举报