news 2026/10/13 3:20:52

【动态规划 -12】300.最长递增子序列

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【动态规划 -12】300.最长递增子序列

题目描述:

给你一个整数数组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

解题思路:

方法一:动态规划(O(n²))

核心思路:

状态定义:

dp[i]= 以nums[i]结尾的最长递增子序列的长度。

状态转移:

对于每个i,枚举j < i:

  • 如果nums[j] < nums[i],说明nums[i]可以接在nums[j]后面

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

初始化:

dp[i] = 1(每个元素自身就是一个长度为 1 的递增子序列)

具体过程示例:

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

inums[i]枚举 jdp[i]
010-1
1910>9,不能接1
22都不能接1
352<5,dp[2]+1=22
432<3,dp[2]+1=22
572<7,5<7,3<7 → max=dp[3]+1=33
6101所有都<101 → max=dp[5]+1=44
7182,5,3,7<18 → max=dp[5]+1=44

最长长度 = 4✅

代码实现:

class Solution { public: int lengthOfLIS(vector<int>& nums) { int n = nums.size(); vector<int> dp(n, 1); int result = 1; for (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { if (nums[j] < nums[i]) { dp[i] = max(dp[i], dp[j] + 1); } } result = max(result, dp[i]); } return result; } };

复杂度分析:

维度复杂度说明
时间复杂度O(n²)双重循环
空间复杂度O(n)dp 数组

方法二:贪心 + 二分查找(O(n log n),最优)

核心思路:

维护一个数组tails:

  • tails[i]= 长度为i+1的递增子序列的最小结尾元素

  • tails是严格递增的

遍历nums,对于每个num:

  • 如果num > tails的最后一个元素,直接追加

  • 否则,用二分查找找到tails中第一个 ≥ num的位置,替换它

最终tails的长度就是最长递增子序列的长度。

具体过程示例:

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

numtails操作
10[10]追加
9[9]替换 10
2[2]替换 9
5[2, 5]追加
3[2, 3]替换 5
7[2, 3, 7]追加
101[2, 3, 7, 101]追加
18[2, 3, 7, 18]替换 101

tails 长度 = 4✅

代码实现:

class Solution { public: int lengthOfLIS(vector<int>& nums) { vector<int> tails; for (int num : nums) { auto it = lower_bound(tails.begin(), tails.end(), num); if (it == tails.end()) { tails.push_back(num); // 比所有元素都大,追加 } else { *it = num; // 替换第一个 ≥ num 的元素 } } return tails.size(); } };

手动实现二分:

class Solution { public: int lengthOfLIS(vector<int>& nums) { vector<int> tails; for (int num : nums) { int left = 0, right = tails.size(); // 找第一个 >= num 的位置 while (left < right) { int mid = left + (right - left) / 2; if (tails[mid] < num) { left = mid + 1; } else { right = mid; } } if (left == tails.size()) { tails.push_back(num); } else { tails[left] = num; } } return tails.size(); } };

复杂度分析:

维度复杂度说明
时间复杂度O(n log n)遍历 n 次,每次二分 O(log n)
空间复杂度O(n)tails 数组

关键细节:

1. 为什么tails是严格递增的?

因为tails[i]表示长度为i+1的递增子序列的最小结尾,长度越长,结尾一定越大。

2. 为什么用lower_bound而不是upper_bound?

因为要求严格递增,所以要找第一个≥ num的位置替换。

  • 严格递增:lower_bound(第一个 ≥ num)

  • 非严格递增:upper_bound(第一个 > num)

3.tails为什么不是最长递增子序列?

tails只是维护了每个长度的最小结尾,替换操作会破坏原始顺序。

例子:nums = [1, 3, 2]

tails 变化: [1] → [1,3] → [1,2] 最终 tails = [1,2],但最长递增子序列是 [1,3] 或 [1,2]
4. 和「最长递增子序列的个数」的区别
题目区别
300. 最长递增子序列求长度
673. 最长递增子序列的个数求个数

两种方法对比:

方法时间复杂度空间复杂度能否求具体序列推荐度
动态规划O(n²)O(n)✅ 可以⭐⭐⭐⭐
贪心 + 二分O(n log n)O(n)❌ 只能求长度⭐⭐⭐⭐⭐

注意:tails数组不是最长递增子序列本身,只是用于计算长度。

总结:

要点说明
核心思想DP O(n²) 或 贪心+二分 O(n log n)
最优解法贪心 + 二分,时间 O(n log n)
关键操作lower_bound找第一个 ≥ num 的位置
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 3:19:45

Simulink风光储并网系统建模与协调控制策略详解

1. 项目概述与整体系统方案选择风电和光伏是当前电力系统中发展最快的两类清洁能源&#xff0c;但两者的出力特性完全不同——风电随风速波动&#xff0c;光伏则受辐照度和温度制约&#xff0c;两者叠加后仍然存在天然间歇性。储能系统的加入就是用来弥补这种波动&#xff0c;把…

作者头像 李华
网站建设 2026/10/13 3:19:26

高精度恒温水浴锅怎么选?从温度稳定性到实验室设备配置的实用指南

恒温水浴锅是实验室常用的温控设备&#xff0c;广泛应用于样品预热、样品孵育、试剂处理、样品解冻和培养基制备等场景。对于生物制药、生命科学研究及质量控制实验而言&#xff0c;温度条件不仅影响操作流程&#xff0c;也可能影响实验结果的重复性。面对不同品牌和型号&#…

作者头像 李华
网站建设 2026/10/13 3:19:16

从零自制PS5手柄通用适配器:协议转换与映射完全指南

“AnyPS5”这个标题&#xff0c;外设折腾圈的人一看就明白——就是想让任意手柄都能在PS5上正常用。索尼主机的配件认证机制一直很封闭&#xff0c;副厂设备想通过系统校验非常麻烦&#xff0c;而这类项目干的正是“把自己伪装成官方手柄”的活。下面这篇内容&#xff0c;我会从…

作者头像 李华
网站建设 2026/10/13 3:19:04

众包任务最少答案数计算:从多数投票到样本量公式的工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/13 3:18:59

C++数字与字符串转换全攻略:性能对比、场景选型与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华