动态规划题解——最长递增子序列【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:
        @cache
        def dfs(i:int)-> int:
            res = 0
            for j in range(i):
                if nums[j] < nums[i]:
                    res = max(res, dfs(j))
            return res+1
        return 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/贪心更适合大数据场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值