动态规划题解——最长递增子序列【LeetCode】记忆化搜索方法
300. 最长递增子序列
一、算法逻辑(逐步思路)
❓ 问题描述:
给定一个整数数组 nums
,找出其中最长严格递增子序列的长度。
✅ 解题思路(DFS + 记忆化)
1. 定义递归函数:
dfs(i) 表示:以 nums[i] 为结尾的最长递增子序列的长度
2. 转移逻辑:
- 对于位置
i
,你要去找0~i-1
所有小于nums[i]
的前缀j
; - 对每个满足
nums[j] < nums[i]
的位置j
:
-
- 当前以
nums[i]
为结尾的序列长度可以是:dfs(j) + 1
- 所以取其中的最大值。
- 当前以
3. 递归基:
dfs(i)
至少为 1,因为每个元素自身就是一个长度为 1 的递增子序列。
4. 最终答案:
- 所有
dfs(i)
中的最大值,即max(dfs(i) for i in range(n))
。
5. 使用 @cache
做记忆化,避免重复递归计算。
二、算法核心点
✅ 核心思想:“以 i 结尾”模型 + 记忆化 DFS
- 每一个位置都试图作为“递增序列的终点”,寻找它前面的合法子结构;
- 这是 LIS 问题的经典思维方式(不同于“以 i 开头”的方式);
- 记忆化可以有效避免指数级递归爆炸。
class Solution:def lengthOfLIS(self, nums: List[int]) -> int:@cachedef dfs(i:int)-> int:res = 0for j in range(i):if nums[j] < nums[i]:res = max(res, dfs(j))return res+1return max(dfs(i) for i in range(len(nums)))
三、复杂度分析
- 时间复杂度:O(n²)
-
- 一共
n
个位置; - 每个
dfs(i)
至多遍历前i
个位置(最多 n 次); - 加上缓存,每个状态只算一次。
- 一共
- 空间复杂度:O(n)
-
- 缓存表大小为
n
; - 递归栈最大深度为
n
。
- 缓存表大小为
总结表:
维度 | 内容 |
✅ 思路逻辑 | 每个位置向前查找比它小的数,递归求以该位置为结尾的 LIS 长度 |
✅ 核心技巧 | 转化为“以 i 结尾”的子问题;记忆化避免重复递归 |
✅ 时间复杂度 | O(n²),两层循环(递归 + 遍历前缀) |
✅ 空间复杂度 | O(n),记忆表和递归栈大小 |
💡 拓展建议
- 如果你想进一步优化时间到 O(n log n),可以使用贪心 + 二分法的经典 LIS 做法(如使用
bisect
插入维护当前最小末尾值序列); - DFS 写法适合初步掌握问题结构,DP/贪心更适合大数据场景。