【记忆化搜索】最长递增子序列

文章目录

题目链接:300. 最长递增子序列


题目解析

记忆化搜索 是一种通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式(是一种常见的动态规划实现方式)。

可以通过记忆化搜索来优化 DFS(剪枝)以提高算法效率。

本题给出一个整数数组 nums,我们需要找到其中最长严格递增子序列的长度并返回(子序列指的是数组中的元素按照删除或不删除且不改变其余元素顺序的序列)。

例如 [3, 6, 2, 7] 是数组 [0, 3, 1, 6, 2, 2, 7] 的子序列。

例1:nums = 10, 9, 2, 5, 3, 7, 101, 18

找出的最长递增子序列是:

  1. 2, 5, 7, 101
  2. 2, 3, 7, 101
  3. 2, 5, 7, 18
  4. 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位置为起始的最长递增子序列长度。

  1. 在递归展开之前,先查看记忆数组/备忘录
  2. 在递归展开之后,先将得到的结果存入记忆数组/备忘录,再返回

代码实现

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;
    }
}

文章到这里就告一段落啦,若有错误请尽管指出~

完

相关推荐
信奥卷王8 分钟前
[GESP202609 六级] 分树规划
算法
Su米苏10 分钟前
Spring AI 中MCP 与普通 @Tool的区别
java·人工智能·spring
夏幻灵15 分钟前
LeetCode 21:合并两个有序链表——双指针与哑节点详解
算法·leetcode·链表
码云数智-园园22 分钟前
unique_ptr 还是 shared_ptr?C++ 智能指针选型与内存泄漏实战分析
java·开发语言
老板一杯拿铁24 分钟前
Java 异常体系详解:从字节码原理到最佳实践
java
蜗牛互联网39 分钟前
Java 17 HttpClient调用文件转写API的超时与失败回退
java·人工智能·后端
禾小西1 小时前
07丨Redis 哨兵机制:主库故障后,如何恢复服务?
java·开发语言·redis
我不是阵雨1 小时前
JDK 21虚拟线程Pinning陷阱:一文拔钉解困
java·开发语言
小卡车5552 小时前
java反射、自定义注解的应用(自定义分页)
java