LeetCode每日一题之最长递增子序列

122 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情

题目描述

2879591e1549ec4fa7abe3711c65888.jpg

题目分析

由题目分析可知,一个非空数组的最长递增子序列的最小值一定是1(即使该数组是一个递减数组),假设一个数组的长度为k,其最长递增子序列长度为len,再像数组后面加入一个数字,那么长度为k+1的数组的最长递增子序列,一定可以由长度为k的数组推导出来,可能为len(加入的数字比递增子序列的最大值小),可能为len+1(加入的数字比递增子序列的最大值大),可由动态规划解决。

状态转移分析

1.dp[i]定义
dp[i] 表示数组nums以nums[i]结尾的子数组的最长上升子序列的长度
2.状态转移公式
如果nums[i] > nums[j]:
位置i的最长上升子序列等于(j从0到i-1各个位置的最长上升子序列+1) 的最大值
dp[i] = Math.max(dp[i],dp[j]+1)  // 求从0-j-1各个位置的最长递增子序列+1取最大值并循环更新dp[i
如果nums[i]<= nums[j]
说明加入nums[i]之后,最长递增子序列的长度不变
3.初始值
一个非空数组的最长递增子序列的最小值一定是1 dp = Array(nums.length).fill(1)
4.遍历顺序
显而易见应该从前往后遍历

代码实现

var lengthOfLIS = function(nums) {
  if(nums.length<=1) return nums.length
  let result = 0;
  const dp = Array(nums.length).fill(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)
          }
      }

      if(dp[i] > result) result = dp[i]
      console.log("i------------", i, dp, result);

  }

  return result
};

以数组 [2,5,3,5,1] 为例
初始化dp = [11111]
result = 0;
第一轮外层循环:
  i = 1:
  内层寻循环:
   j = 0;  5>2  =>    dp[i] = Math.max(1,1+1) = 2
   dp = [1,2,1,1,1]
   result = max(result,dp[1]) = 2;
   循环结束
第二层外层循环
  i = 2:
  内层循环:
  j = 0;   3>2  =>    dp[i] = max(1,1+1) => 2
  j = 1;   3<5  =>   不处理
  dp = [1,2,2,1,1]
  result = max(result,dp[2]) = 2;
  循环结束
 第三册外层循环:
 i = 3:
 内层循环:
  j = 0: 5>2  =>      dp[i] = max(1,1+1) => 2
  j = 1  5=5  =>      不处理
  j = 2  5>3  =>      dp[i] = max(1,2+1) => 3
  循环结束
  dp = [1,2,2,3,1]
  result = max(result,dp[3]) = 3;
 第四层外层循环:
  i = 4:
  内层循环:
    j = 0;  1 < 2  => 不处理
    j = 1;  1 < 5  => 不处理
    j = 2;  1 < 3  => 不处理
    j = 3;  1 < 5  => 不处理
  dp = [1,2,2,3,1]
  result = max(result,dp[4]) = 3;
  

运行截图

4f0eb119a52f8139be8d061e586c1a4.jpg
以上为个人理解,欢迎各位大佬指正。