当前位置: 首页 > web >正文

动态规划求解leetcode300.最长递增子序列(LIS)详解

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

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

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

关键点

  • 双重循环结构:外层循环遍历每个元素作为子序列结尾,内层循环检查所有可能的前驱元素

  • 递增条件:只有当 nums[j] < nums[i] 时才考虑状态转移

  • 时间复杂度:O(n²),因为有两层嵌套循环

核心思路

  1. 定义状态

    • dp[i] 表示以 nums[i] 结尾的最长递增子序列的长度

    • 初始时每个元素本身就是一个长度为1的子序列,所以初始化 dp 数组全为1

  2. 状态转移

    • 对于每个 i(当前元素),检查所有 j < i(前面的元素)

    • 如果 nums[j] < nums[i](满足递增条件),则尝试更新 dp[i]

      dp[i] = Math.max(dp[i], dp[j] + 1)

      这表示如果接在 nums[j] 后面能形成更长的子序列,就更新 dp[i]

  3. 记录结果

    • 在计算过程中持续维护一个全局最大值 res

    • 最终 res 就是整个数组的最长递增子序列长度

// Dynamic programming.
class Solution {public int lengthOfLIS(int[] nums) {if(nums.length == 0) return 0;int[] dp = new int[nums.length];int res = 0;Arrays.fill(dp, 1);for(int i = 0; i < nums.length; i++) {for(int j = 0; j < i; j++) {if(nums[j] < nums[i]) dp[i] = Math.max(dp[i], dp[j] + 1);}res = Math.max(res, dp[i]);}return res;}
}

示例数组

nums = [10, 9, 2, 5, 3, 7, 101, 18]

算法步骤解析

  1. 初始化

    • 创建dp数组,长度与nums相同,初始值全为1(因为每个元素本身就是一个长度为1的子序列)

    dp = [1, 1, 1, 1, 1, 1, 1, 1]
  2. 双重循环处理

    • 外层循环:i从0到n-1

    • 内层循环:j从0到i-1

  3. 逐步计算过程

    i=0 (nums[0]=10):

    • j无(因为i=0时j从0到-1)

    • dp保持不变:[1, 1, 1, 1, 1, 1, 1, 1]

    • res=1

    i=1 (nums[1]=9):

    • j=0: nums[0]=10 > nums[1]=9 → 不更新

    • dp保持不变:[1, 1, 1, 1, 1, 1, 1, 1]

    • res=1

    i=2 (nums[2]=2):

    • j=0: nums[0]=10 > nums[2]=2 → 不更新

    • j=1: nums[1]=9 > nums[2]=2 → 不更新

    • dp保持不变:[1, 1, 1, 1, 1, 1, 1, 1]

    • res=1

    i=3 (nums[3]=5):

    • j=0: nums[0]=10 > nums[3]=5 → 不更新

    • j=1: nums[1]=9 > nums[3]=5 → 不更新

    • j=2: nums[2]=2 < nums[3]=5 → dp[3] = max(dp[3], dp[2]+1) = max(1, 2) = 2

    • dp更新为:[1, 1, 1, 2, 1, 1, 1, 1]

    • res=2

    i=4 (nums[4]=3):

    • j=0: nums[0]=10 > nums[4]=3 → 不更新

    • j=1: nums[1]=9 > nums[4]=3 → 不更新

    • j=2: nums[2]=2 < nums[4]=3 → dp[4] = max(dp[4], dp[2]+1) = max(1, 2) = 2

    • j=3: nums[3]=5 > nums[4]=3 → 不更新

    • dp更新为:[1, 1, 1, 2, 2, 1, 1, 1]

    • res=2

    i=5 (nums[5]=7):

    • j=0: nums[0]=10 > nums[5]=7 → 不更新

    • j=1: nums[1]=9 > nums[5]=7 → 不更新

    • j=2: nums[2]=2 < nums[5]=7 → dp[5] = max(dp[5], dp[2]+1) = max(1, 2) = 2

    • j=3: nums[3]=5 < nums[5]=7 → dp[5] = max(dp[5], dp[3]+1) = max(2, 3) = 3

    • j=4: nums[4]=3 < nums[5]=7 → dp[5] = max(dp[5], dp[4]+1) = max(3, 3) = 3

    • dp更新为:[1, 1, 1, 2, 2, 3, 1, 1]

    • res=3

    i=6 (nums[6]=101):

    • j=0: nums[0]=10 < nums[6]=101 → dp[6] = max(dp[6], dp[0]+1) = max(1, 2) = 2

    • j=1: nums[1]=9 < nums[6]=101 → dp[6] = max(dp[6], dp[1]+1) = max(2, 2) = 2

    • j=2: nums[2]=2 < nums[6]=101 → dp[6] = max(dp[6], dp[2]+1) = max(2, 2) = 2

    • j=3: nums[3]=5 < nums[6]=101 → dp[6] = max(dp[6], dp[3]+1) = max(2, 3) = 3

    • j=4: nums[4]=3 < nums[6]=101 → dp[6] = max(dp[6], dp[4]+1) = max(3, 3) = 3

    • j=5: nums[5]=7 < nums[6]=101 → dp[6] = max(dp[6], dp[5]+1) = max(3, 4) = 4

    • dp更新为:[1, 1, 1, 2, 2, 3, 4, 1]

    • res=4

    i=7 (nums[7]=18):

    • j=0: nums[0]=10 < nums[7]=18 → dp[7] = max(dp[7], dp[0]+1) = max(1, 2) = 2

    • j=1: nums[1]=9 < nums[7]=18 → dp[7] = max(dp[7], dp[1]+1) = max(2, 2) = 2

    • j=2: nums[2]=2 < nums[7]=18 → dp[7] = max(dp[7], dp[2]+1) = max(2, 2) = 2

    • j=3: nums[3]=5 < nums[7]=18 → dp[7] = max(dp[7], dp[3]+1) = max(2, 3) = 3

    • j=4: nums[4]=3 < nums[7]=18 → dp[7] = max(dp[7], dp[4]+1) = max(3, 3) = 3

    • j=5: nums[5]=7 < nums[7]=18 → dp[7] = max(dp[7], dp[5]+1) = max(3, 4) = 4

    • j=6: nums[6]=101 > nums[7]=18 → 不更新

    • dp更新为:[1, 1, 1, 2, 2, 3, 4, 4]

    • res=4

最终结果

最长递增子序列的长度为4。对应的子序列可以是:

  • [2, 3, 7, 101]

  • 或 [2, 5, 7, 101]

  • 或 [2, 3, 7, 18]

  • 或 [2, 5, 7, 18]

 

http://www.xdnf.cn/news/2144.html

相关文章:

  • React 与 Vue 的区别:你会选择哪个框架呢
  • 关于Android Studio的Gradle各项配置
  • 高级 SQL 技巧:提升数据处理能力的实用方法
  • 图像畸变-径向切向畸变实时图像RTSP推流
  • leetcode 26和80
  • strcmp()在C语言中怎么用(附带实例)
  • CentOS 如何使用截图工具截取命令行操作的图片?
  • 定制一款国密浏览器(12):分析SM2签名算法的实现
  • 在 Linux 上安装 PNPM 的教程
  • Git分支重命名与推送参数解析
  • 案例速成GO操作redis,个人笔记
  • LeetCode100题
  • 案例速成GO+redis 个人笔记
  • 【springboot知识】配置方式实现SpringCloudGateway相关功能
  • TortoiseGit 入门指南
  • Linux基础命令总结
  • 【设计模式区别】装饰器模式和适配器模式区别
  • C#中wpf程序中的x名空间详解
  • CSS3布局方式介绍
  • 如何修改npm的全局安装路径?
  • 【Token系列】02 | Embedding是怎么“长出来”的?从查表到训练过程全解
  • git和github的使用指南
  • 探索具身智能协作机器人:技术、应用与未来
  • 苹果(IOS)手机怎么开启开发者模式(简单明了版)
  • 在QML中获取当前时间、IP和位置(基于网络请求)
  • 机器学习:逻辑回归实现二元分类
  • 【解决】trying to draw too large(147456000bytes) bitmap
  • 当自动驾驶遇上“安全驾校”:NVIDIA如何用技术给无人驾驶赋能?
  • Redis和MQ的区别
  • WEB安全--RCE--webshell bypass