【LeetCode】300. 最长递增子序列
解题思路
寻找最长严格递增子序列(LIS)是动态规划和贪心算法的经典问题。核心思路如下:
-
动态规划解法(O(n²)):
- 定义
dp[i]表示以nums[i]结尾的 LIS 长度。 - 对每个
i,遍历j∈[0, i-1],若nums[j] < nums[i],则更新dp[i] = max(dp[i], dp[j]+1)。 - 最终结果为
dp数组的最大值。
- 定义
-
贪心+二分优化(O(n log n)):
- 维护数组
d[],其中d[k]表示长度为k的 LIS 的最小末尾值。 - 遍历数组,对每个数用二分查找在
d中定位:- 若大于所有
d[k],则扩展序列长度。 - 否则替换
d中第一个大于等于它的值,保持序列增长潜力。
- 若大于所有
- 维护数组
关键步骤
动态规划
- 初始化:
dp数组全设为 1(每个元素自身是长度为 1 的子序列)。 - 双层循环:
- 外层遍历
i∈[1, n-1]。 - 内层遍历
j∈[0, i-1],若nums[j] < nums[i],则更新dp[i] = max(dp[i], dp[j]+1)。
- 外层遍历
- 取最大值:遍历
dp数组,返回最大值。
贪心+二分
- 初始化:空数组
d,长度len=0。 - 遍历数组:
- 若
nums[i] > d[len],则d = append(d, nums[i]),len++。 - 否则二分查找
d中第一个≥ nums[i]的位置pos,更新d[pos] = nums[i]。
- 若
- 返回结果:
len即 LIS 长度。
代码实现
方法 1:动态规划(O(n²))
方法 2:贪心+二分(O(n log n))
示例测试
复杂度分析
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 动态规划(DP) | O(n²) | O(n) | 代码简单,n ≤ 10³ |
| 贪心+二分(优化) | O(n log n) | O(n) | 高效,n ≤ 10⁵ |
关键点
-
动态规划本质:
dp[i]依赖子问题dp[j](j < i),需遍历所有子问题。- 状态转移方程:
dp[i] = max(dp[i], dp[j] + 1)(当nums[j] < nums[i])。
-
贪心策略:
- 维护最小末尾值数组
d[],使序列增长尽可能慢。 - 二分查找(
sort.SearchInts)确保定位效率(O(log n))。
- 维护最小末尾值数组
-
二分查找细节:
- 替换
d[pos]保证相同长度下末尾值最小,提升后续扩展可能性。 - 不改变
d的严格递增性(数学可证)。
- 替换
-
适用性:
- DP 适合小规模数据(n ≤ 2000)。
- 贪心+二分适用于大规模数据(n ≤ 10⁵),如 LeetCode 最大测试用例(n = 2500)。

浙公网安备 33010602011771号