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

文章目录

题目链接: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;
    }
}

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

完

相关推荐
学长毕设1 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
zzu123zsw2 小时前
从臃肿的模板调用到声明式契约:深入 UniHttp 动态代理与多渠道生命周期扩展
java·spring boot·microsoft·unihttp
UIU1143 小时前
P1233 [ICPC 2001 Taejon R] 木棍加工
数据结构·c++·算法·动态规划
源码集结号3 小时前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统
论文复现现场3 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
ZhengEnCi3 小时前
用 AI 写代码为什么拼到最后还是拼人:算力瓶颈与领域专家提示词的本质差异
算法·架构
啃瓜子的松鼠4 小时前
泛微OAe9-后端二开异步调用
java
秋天的怀念_David4 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
外收内放5 小时前
Python基础语法练习题(73)
java·前端·python
Wang's Blog5 小时前
Java 项目实战: 交友项目 - 环信后台发送消息测试
java·交友