文章目录
题目链接:300. 最长递增子序列
题目解析
记忆化搜索 是一种通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式(是一种常见的动态规划实现方式)。
可以通过记忆化搜索来优化 DFS(剪枝)以提高算法效率。
本题给出一个整数数组 nums,我们需要找到其中最长严格递增子序列的长度并返回(子序列指的是数组中的元素按照删除或不删除且不改变其余元素顺序的序列)。
例如 [3, 6, 2, 7] 是数组 [0, 3, 1, 6, 2, 2, 7] 的子序列。
例1:nums = 10, 9, 2, 5, 3, 7, 101, 18
找出的最长递增子序列是:
- 2, 5, 7, 101
- 2, 3, 7, 101
- 2, 5, 7, 18
- 2, 3, 7, 18
以上四种情况均是例1的最长递增子序列,且长度都为 4,因此返回 4。
DFS:深度优先搜索
算法原理
最简单的方式就是:遍历nums数组,将每个数逐个当作起点,找到以该数为起点的递增子序列的最大值。
全局变量
为了递归方便,将 nums 改为全局变量,同时 n 为数组 nums 的长度。
java
int[] nums;
int n;
dfs 函数
函数头
我们给 dfs 函数的任务是:找到以pos位置为起点的递增子序列的最大值 ,其中 pos 为函数的参数,表示下标位置,返回值为 int。
java
int dfs (int pos);
函数体
dfs 函数的具体步骤是:从pos位置往后找比当前数字更大的数,然后获取它的 dfs 值(以该数为起点的最长子序列长度)并更新自己的用于记录长度的变量。
当遍历完 nums 数组,递归结束,返回结果即可。
代码实现
java
class Solution {
int[] nums;
int n;
public int lengthOfLIS(int[] givenNums) {
// 初始化
nums = givenNums;
n = nums.length;
int ret = 0; // 记录最长子序列
// 遍历nums,以每个位置为起点找到所有递增子序列的最大值
for (int i = 0; i < n; i++) {
ret = Math.max(dfs(i), ret); // 更新结果
}
// 返回结果
return ret;
}
// dfs任务:找到以pos位置为起点的递增子序列长度的最大值
public int dfs (int pos) {
int count = 0;
for (int k = pos+1; k < n; k++) {
if (nums[k] > nums[pos]) {
count = Math.max(dfs(k), count);
}
}
count++;
// 返回结果
return count;
}
}
记忆化搜索
使用递归法会超时,因为整个递归的展开图是一棵多叉树,时间复杂度相当高。
例如,nums = 2, 5, 3, 7, 101, 18,画出的比分递归展开图:

可以发现有很多重复展开的过程,这是导致超时的最主要原因,我们可以想办法剪枝以提高效率。
算法原理
记忆化搜索的本质就是 "带着记忆递归搜索" ,而 记忆数组/备忘录 正是这类问题的关键。
记忆数组
本题针对 pos 进行记忆化处理,需要一个一维数组 当作记忆数组,值为 以pos位置为起始的最长递增子序列长度。
- 在递归展开之前,先查看记忆数组/备忘录
- 在递归展开之后,先将得到的结果存入记忆数组/备忘录,再返回
代码实现
java
class Solution {
int[] nums;
int n;
int[] memo; // 记忆数组
public int lengthOfLIS(int[] givenNums) {
// 初始化
nums = givenNums;
n = nums.length;
memo = new int[n];
int ret = 0; // 记录最长子序列
// 遍历nums,以每个位置为起点找到所有递增子序列的最大值
for (int i = 0; i < n; i++) {
ret = Math.max(dfs(i), ret); // 更新结果
}
// 返回结果
return ret;
}
// dfs任务:找到以pos位置为起点的递增子序列长度的最大值
public int dfs (int pos) {
// 递归展开之前,先查看记忆数组
if (memo[pos] != 0) {
return memo[pos];
}
int count = 0;
for (int k = pos+1; k < n; k++) {
if (nums[k] > nums[pos]) {
count = Math.max(dfs(k), count);
}
}
count++;
// 递归展开之后,先将结果存入记忆数组再返回
memo[pos] = count;
return count;
}
}
文章到这里就告一段落啦,若有错误请尽管指出~
完