算法【Java】—— 记忆化搜索

news2024/12/23 10:29:50

记忆化搜索

在前面我们已经学习了递归回溯等知识,什么是记忆化搜索,其实就是带着备忘录的递归,我们知道在递归过程中如果如果出现大量的重复的相同的子问题的时候,我们可能进行了多次递归,但是这些递归其实可以只用进行一次,我们只需要将结果保存起来,然后在进行递归之前先往备忘录里查看是否已经递归过了,如果是直接从备忘录里拿取结果,如果不是则进行递归。

注意:不是所有的递归都能使用记忆化搜索,只有遇到的子问题是一模一样的才能这样做!!!

实战演练

斐波那契额数

在这里插入图片描述


熟悉的题目,我们知道可以使用递归来做,但是大家最开始写的递归版本大概率时间复杂度是 O(2^n),这是因为递归次数太多了,并且出现很多重复的递归,这时候我们如果使用记忆化搜索,创建一个备忘录,在递归之前先看看备忘录里有没有这个数值,如果没有才进行递归,递归结束后顺便将结果填入备忘录。这样时间复杂度就被我们优化为 O(N),空间复杂度为 O(N)

class Solution {
    //创建备忘录
    int[] arr;
    public int fib(int n) {
        arr = new int[n + 1];
        Arrays.fill(arr, -1);
        return dfs(n);
    }
    int dfs(int n) {
        if(n == 0 || n == 1) {
            arr[n] = n;
            return n;
        }
        //往备忘录里检查一下
        if(arr[n] != -1) {
            return arr[n];
        }
        int ret = dfs(n - 1) + dfs(n - 2);
        arr[n] = ret;
        return ret;
    }
}

不同路径

在这里插入图片描述


在这里插入图片描述

我们知道机器人只能向右或者向下移动,要想知道到达目的地的路径数目,我们可以通过 达到 1 号地点的路径 数目 加上 2 号路径的数目的结果来获取。要想知道达到 1 号路径同样也要知道 1 号路径上面的 3 号路径的数目和左边 4 号路径的数目。好了,相同的一模一样的子问题出来了,我们可以使用记忆化搜索了。

从上面的分析,我们知道我们要使用逆向思维了,我们的递归开始地点应该从 目的地开始。
然后递归上面 和 左边也就是说 dfs(x, y) = dfs(x-1, y) + dfs(x, y-1)

我们赋予递归一个牛逼的使命,递归的任务直接返回一共有多少条路径数目。
递归的出口:dfs(x-1, y) + dfs(x, y-1) 这条式子获得的新的 x 和 y 都可能为负数,也就是会出现越界访问的情况,这就是递归的出口。
记忆化的处理:递归之前先检查备忘录,递归之后的结果同时保存到备忘录里。

class Solution {
    //创建备忘录
    int[][] memo;
    public int uniquePaths(int m, int n) {
        memo = new int[m][n];
        return dfs(m - 1, n - 1);
    }
    int dfs(int x, int y) {
        //避免越界
        if(x == -1 || y == -1) {
            return 0;
        }
        if(memo[x][y] != 0) {
            return memo[x][y];
        }
        if(x == 0 && y == 0) {
            memo[x][y] = 1;
            return 1;
        }
        memo[x][y] = dfs(x-1 ,y) + dfs(x, y-1);

        return memo[x][y];
    }
}

最长递增子序列

在这里插入图片描述


我们会遍历数组每一个元素,然后以每一个元素为基础进行递归搜索看看最长递增子序列的长度。

我们用备忘录来保存每一个元素所处的最大递增子序列。

class Solution {
    //创建备忘录
    int[] memo;
    int n;
    public int lengthOfLIS(int[] nums) {
        n = nums.length;
        memo = new int[n];
        int sum = 0;
        for(int i = 0; i < n; i++) {
            sum = Math.max(dfs(nums, i), sum);
        }
        return sum;
    }
    int dfs(int[] nums, int str) {
        if(memo[str] != 0) {
            return memo[str];
        }
        int sum = 1;
        for(int i = str + 1; i < n; i++) {
            if(nums[i] > nums[str]) {
                sum = Math.max(sum, dfs(nums, i) + 1);
            }
        }
        memo[str] = sum;
        return memo[str];
    }
}

猜数字大小 Ⅱ

在这里插入图片描述


题目解析:猜数字的范围 是 1 ~ n,我们要从中找到最小花费,也就是从哪个数字开始猜是最小的花费,当我们开始从这个数字开始猜的时候,我们要找到这里面最大的花费,才能保证我们从这个数字开始猜一定够钱猜。

从中我们知道我们要先遍历 1 ~ n 找到最小花费,然后从某个数字开始猜的时候,有两种可能,一个是猜大了,另一个是猜小了,然后从这两种情况找到最大花费的情况。

我们定义一个递归函数,赋予其一个使命:从 1 ~ n 猜返回最小的花费,我们使用一个二维备忘录来记录数据,首先,每次猜数字都有两种情况(猜大了或者猜小了)这时候又要进行大范围或者小范围的递归搜索,所以使用二维数组进行保存结果。

class Solution {
    //创建备忘录
    int[][] memo;
    int n;
    public int getMoneyAmount(int _n) {
        n = _n;
        memo = new int[n + 1][n + 1];
        return dfs(1, n);
    }
    int dfs(int left, int right) {
        if(left >= right) { 
            return 0;
        }
        if(memo[left][right] != 0) {
            return memo[left][right];
        }

        int ret = Integer.MAX_VALUE;
        for(int head = left; head <= right; head++) {
            int x = dfs(left, head - 1);//猜小了
            int y = dfs(head + 1, right);//猜大了
            ret = Math.min(Math.max(x, y) + head, ret);
        }
        memo[left][right] = ret;
        return ret;
    }
}

矩阵中的最长递增路径

在这里插入图片描述


这个题目就是之前我们使用的 FloodFill 算法,只不过这里使用了备忘录进行了优化。

class Solution {
    //创建备忘录
    int[][] memo;
    int[] dx = {0, 0, 1, -1};
    int[] dy = {1, -1, 0, 0};
    int m, n;
    public int longestIncreasingPath(int[][] matrix) {
        m = matrix.length;
        n = matrix[0].length;
        memo = new int[m][n];
        int sum = 0;
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                sum = Math.max(dfs(matrix, i, j), sum);
            }
        }
        return sum;
    }
    int dfs(int[][] matrix, int i, int j) {
        if(memo[i][j] != 0) {
            return memo[i][j];
        }
        int path = 1;
        for(int k = 0; k < 4; k++) {
            int x = dx[k] + i;
            int y = dy[k] + j;
            if(x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j]) {
                path = Math.max(path, dfs(matrix, x, y) + 1);
            }
        }
        memo[i][j] = path;
        return path;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2231613.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

优化EDM邮件营销,送达率与用户体验双赢

EDM邮件营销需选对平台&#xff0c;优化邮件列表&#xff0c;确保内容优质&#xff0c;进行邮件测试&#xff0c;关注用户反馈调整频率&#xff0c;以保高送达率&#xff0c;提升营销效果。 1. 了解电子邮件送达率的重要性 在开始优化邮件送达率之前&#xff0c;首先需要理解电…

Metasploit渗透测试之在云服务器中使用MSF

概述 随着云计算的发展&#xff0c;对基于云的应用程序、服务和基础设施的测试也在不断增加。在对云部署进行渗透测试时&#xff0c;最大的问题之一是共享所有权。过去&#xff0c;在进行渗透测试时&#xff0c;企业会拥有网络上的所有组件&#xff0c;我们可以对它们进行全部…

Qt桌面应用开发 第一天

目录 1.默认代码解析 1.1main.h解析 1.2myWidget.h解析 1.3FirstProject.pro解析&#xff08;FirstProject为创建的Qt项目名&#xff09; 2.命名规范与快捷键 3.按钮控件及窗口设置 3.1按钮控件QPushButton类 3.2窗口常用设计 4.Qt中的对象树 5.Qt中的坐标系 Qt是一个…

简记Vue3(三)—— ref、props、生命周期、hooks

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

Mybatis查询数据库,返回List集合,集合元素也是List。

#有时间需求会要求&#xff1a;查询全校的学生数据&#xff0c;且学生数据按班级划分。那么就需要List<List<user>>类型的数据。 SQL语句 SELECT JSON_ARRAYAGG(JSON_OBJECT(name , name ,BJMC, BJMC ,BJBH,BJBH)) as dev_user FROM dev_user WHERE project_id …

Freertos学习日志(1)-基础知识

目录 1.什么是Freertos&#xff1f; 2.为什么要学习RTOS&#xff1f; 3.Freertos多任务处理的原理 1.什么是Freertos&#xff1f; RTOS&#xff0c;即&#xff08;Real Time Operating System 实时操作系统&#xff09;&#xff0c;是一种体积小巧、确定性强的计算机操作系统…

批量提取当前文件夹内的文件名

在需要提取的文件夹内新建一个txt文件&#xff0c;输入&#xff1a; dir ./b>name.txt 然后将该txt文件的扩展名改为.bat 如图 双击即可提取当前文件夹文件名&#xff0c;并保存到name.txt内

小林渗透入门:burpsuite+proxifier抓取小程序流量

目录 前提&#xff1a; 代理&#xff1a; proxifier&#xff1a; 步骤&#xff1a; bp证书安装 bp设置代理端口&#xff1a; proxifier设置规则&#xff1a; proxifier应用规则&#xff1a; 结果&#xff1a; 前提&#xff1a; 在介绍这两个工具具体实现方法之前&#xff0…

Node.js:session JWT

Node.js&#xff1a;session & JWT sessioncookiesessionexpress-session JWTexpress-jwt & jsonwebtoken session HTTP协议是无状态的&#xff0c;客户端的每次HTTP请求都是独立的&#xff0c;多个请求之间没有直接的关系&#xff0c;服务器不会保留每次HTTP请求的状…

后台管理系统的通用权限解决方案(七)SpringBoot整合SpringEvent实现操作日志记录(基于注解和切面实现)

1 Spring Event框架 除了记录程序运行日志&#xff0c;在实际项目中一般还会记录操作日志&#xff0c;包括操作类型、操作时间、操作员、管理员IP、操作原因等等&#xff08;一般叫审计&#xff09;。 操作日志一般保存在数据库&#xff0c;方便管理员查询。通常的做法在每个…

曹操出行借助 ApsaraMQ for Kafka Serverless 提升效率,成本节省超 20%

本文整理于 2024 年云栖大会主题演讲《云消息队列 ApsaraMQ Serverless 演进》&#xff0c;杭州优行科技有限公司消息中间件负责人王智洋分享 ApsaraMQ for Kafka Serverless 助力曹操出行实现成本优化和效率提升的实践经验。 曹操出行&#xff1a;科技驱动共享出行未来 曹操…

Mysql常用语法一篇文章速成

文章目录 前言前置环境数据库的增删改查查询数据查询所有条件查询多条件查询模糊查询分页查询排序查询分组查询⭐️⭐️关联查询关联分页查询 添加数据insert插入多条记录不指定列名(适用于所有列都有值的情况) 更新数据更新多条记录更新多个列更新不满足条件的记录 删除统计数…

信息安全数学基础(43)理想和商环

理想&#xff08;Ideal&#xff09; 定义&#xff1a; 设R是一个环&#xff0c;I是R的一个非空子集。如果I满足以下条件&#xff0c;则称I为R的一个理想&#xff1a; 对于任意的r1, r2 ∈ I&#xff0c;有r1 - r2 ∈ I&#xff08;加法封闭性&#xff09;。对于任意的r ∈ I&am…

node.js下载、安装、设置国内镜像源(永久)(Windows11)

目录 node-v20.18.0-x64工具下载安装设置国内镜像源&#xff08;永久&#xff09; node-v20.18.0-x64 工具 系统&#xff1a;Windows 11 下载 官网https://nodejs.org/zh-cn/download/package-manager 版本我是跟着老师选的node-v20.18.0-x64如图选择 Windows、x64、v20.18…

《JVM第3课》运行时数据区

无痛快速学习入门JVM&#xff0c;欢迎订阅本免费专栏 运行时数据区结构图如下&#xff1a; 可分为 5 个区域&#xff0c;分别是方法区、堆区、虚拟机栈、本地方法栈、程序计数器。这里大概介绍一下各个模块的作用&#xff0c;会在后面的文章展开讲。 类加载子系统会把类信息…

苏州金龙新V系客车创新引领旅游出行未来

10月25日&#xff0c;为期三天的“2024第六届旅游出行大会”在风景秀丽的云南省丽江市落下帷幕。本次大会由中国旅游车船协会主办&#xff0c;全面展示了中国旅游出行行业最新发展动态和发展成就&#xff0c;为旅游行业带来全新发展动力。 在大会期间&#xff0c;备受瞩目的展车…

QML旋转选择器组件Tumbler

1. 介绍 Tumbler是一个用于创建旋转选择器的组件。它提供了一种直观的方式来让用户从一组选项中进行选择&#xff0c;类似于转盘式数字密码锁。网上找的类似网图如下&#xff1a; 在QML里&#xff0c;这种组件一共有两个版本&#xff0c;分别在QtQuick.Extras 1.4(旧)和QtQuic…

思科路由器静态路由配置

转载请注明出处 该实验为静态路由配置实验&#xff0c;仅供参考 选择三台2811路由器 关闭电源-安装模块-开启电源&#xff08;以R1为例&#xff0c;其他两台也是一样操作&#xff01;&#xff09; 连线。注意R1与R3之间、R3与R2之间用DCE串口线&#xff08;如下图&#xff09;…

闪存学习_2:Flash-Aware Computing from Jihong Kim

闪存学习_2&#xff1a;Flash-Aware Computing from Jihong Kim【1】 一、三个闪存可靠性问题二、内存的分类三、NAND 闪存和 NOR 闪存四、HDD和SSD比较Reference 一、三个闪存可靠性问题 耐性&#xff08;即寿命&#xff09;&#xff1a;最多能经受编程和擦除的次数。数据保留…

双指针问题解法集(一)

1.指针对撞问题&#xff08;利用有序数组的单调性衍生&#xff09; 1.1LCR 179. 查找总价格为目标值的两个商品 - 力扣&#xff08;LeetCode&#xff09;​​​​​​ 1.1.1题目解析 有序数组&#xff0c;找到和为price的两个元素&#xff0c;只需要一个解即可。 1.1.2算法…