动态规划-最长递增子序列

754 阅读2分钟

先发自拍抗压!

正题

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

image.png

概念:

子序列:子序列相对于字串不要求连续, 如 [1,5,2,3,4] , 那么 1,2,3,4 为子序列,即使中间夹5,但他们依然是生序子序列。

严格递增: 严格递增即依次元素必须为递增,不包含相等,如: [1,2,3,4] 为严格递增. [1,2,2,3,4] 非严格递增

这样的需求如果使用枚举暴力破解似乎可以达到目的,但细节要求太多,而且复杂度过高,消耗性能极大,所以不作暴力破解的算法研究。

分析:

image.png

以上图为例,

一眼看去,从10开始,有:

10,101 长度为2

10,18 长度为2

9,101 长度为2

9,18 长度为2

2,5,7,101 长度为4

2,5,7,18 长度为4

2,3,7,101 长度为4

2,3,7,18 长度为4
.
.
.
等等

由上可知,我到达 101 这个元素的时候,其长度可能最大为 4。到达18这个元素时,其长度最大可能为4.那么可以记录下到达每一个元素的可能的最大序列长度,然后比较这些长度的大小,即可完成该问题

如何计算到达每个元素下的最大可能长度?

答案是:动态规划!

动态规划的基本概念:动态规划过程是每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。

如何实现动态规划

image.png

创建和目标数组相同大小的数组dbList,每个元素的值为1,为什么是1呢,因为在没有计算和遍历之前,默认每个元素本身就是一个子序列,它的长度为1

通过内外循环两两比对,更改 dpList 对应数值,即修改到达该元素需要的最大长度。

动态规划图解:

ScreenRecorderProject5_1.gif

动图时间有点长,看完之后应该就对动态规划有一定了解!

代码实现:

/**
 * @param {number[]} nums
 * @return {number}
 */
var lengthOfLIS = function (nums) {
  let dp = new Array(nums.length).fill(1)
  for (let i = 1; i < nums.length; i++) { // 外循环
    const right = nums[i]
    for (let j = 0; j < i; j++) { // 内循环
      const left = nums[j]
      if (left < right) {
        dp[i] = Math.max(dp[i], dp[j] + 1) //记录长度
      }
    }
  }

  return Math.max(...dp) // 取得最大长度
};

console.log(lengthOfLIS([0, 1, 0, 3, 2, 3]))