一.题目

二.思路讲解
2.1 思路讲解
本题要求最长递增子序列的长度,可以采用递归 + 记忆化搜索的经典方式。
-
递归定义 :设
dfs(pos)表示从下标pos开始(必须包含nums[pos])的最长递增子序列的长度。那么它等于 1(自身)加上所有满足nums[i] > nums[pos]的位置i中dfs(i)的最大值。 -
递归基 :对于最后一个元素,
dfs(pos) = 1。 -
记忆化优化 :直接递归会导致大量重复计算,因此引入一个备忘录数组
memo,在计算dfs(pos)前先查表,若已计算则直接返回,否则递归计算后存入备忘录。 -
最终答案 :遍历所有位置作为起点,取
dfs(pos)的最大值。
三.代码演示
cpp
class Solution {
public:
int lengthOfLIS(vector<int>& nums)
{
vector<int> memo(nums.size());
int ret = 0;
for(int i = 0;i < nums.size();i++)
{
ret = max(ret,dfs(nums,i,memo));
}
return ret;
}
int dfs(vector<int>& nums,int pos,vector<int>& memo)
{
if(memo[pos] != 0)
return memo[pos];
int ret = 1;//算上自己
for(int i = pos + 1;i < nums.size();i++)
{
//后者严格大于前者
if(nums[i] > nums[pos])
{
ret = max(ret,dfs(nums,i,memo) + 1);
}
}
memo[pos] = ret;//跑到尽头了
return memo[pos];
}
};
四.代码讲解
一、备忘录设计
为了消除重复计算,我们使用一个一维数组 memo,其长度与 nums 相同,memo[pos] 表示以 nums[pos] 作为子序列起点(必须包含该元素)的最长严格递增子序列的长度。初始值均为 0,表示尚未计算。
二、主函数 lengthOfLIS
-
创建一个大小为
nums.size()的memo数组,并初始化为0。 -
遍历所有可能的下标
i,计算以nums[i]开头的最长递增子序列长度dfs(nums, i, memo),并更新全局最大值ret。 -
返回
ret。
三、递归函数 dfs
dfs(nums, pos, memo) 返回以 nums[pos] 开头的最长递增子序列的长度。执行流程如下:
1. 查备忘录
如果 memo[pos] != 0,说明该子问题已经计算过,直接返回存储的值。这是记忆化搜索的核心优化。
2. 初始化当前结果
将 ret 初始化为 1,因为子序列至少包含自身。
3. 向后枚举可能的后续元素
从 i = pos + 1 开始遍历到数组末尾,如果 nums[i] > nums[pos],说明 i 可以接在 pos 之后构成更长的递增子序列。此时递归计算以 i 开头的子序列长度,即 dfs(nums, i, memo) + 1,并用 max 更新 ret。
4. 存入备忘录并返回
将计算得到的 ret 存入 memo[pos],然后返回 ret。
四、关键细节
-
递归定义 :
dfs(pos)必须包含nums[pos],因此递归基隐含在循环中:当pos为最后一个元素时,循环不执行,ret保持为1。 -
记忆化作用 :每个下标
pos只计算一次,之后直接查表返回,避免了大量重复递归。原本的暴力递归时间复杂度为 O(2^n) ,优化后降为 O(n²)。
五、流程图
