首页 > 编程语言 >数据结构与算法 | 记忆化搜索(Memorize Search)

数据结构与算法 | 记忆化搜索(Memorize Search)

时间:2023-11-13 09:11:06浏览次数:40  
标签:search matrix Memorize int max 单元格 Search 记忆 数据结构

在本系列的文章中已经写了二叉树(Binary Tree)深搜(DFS)与广搜(BFS)哈希表(Hash Table)等等,计划接下来要写的是动态规划(Dynamic Programming,DP),它算得上是最灵活的一种算法。回忆笔者学习动态规划的时候,最开始接触的是经典的 “01背包” 问题;不过现在想起来,以“01背包问题”作为初次接触的动态规划算法的问题_并不友好_;花费了不少时间才慢慢感悟到动态规划算法的核心思想。

先前的文章中涉及了不少搜索算法,在搜索算法上融入动态规划算法思想的 记忆化搜索(Memorize Search)不妨是一个不错的_承前启后_的选择。相对于 “01背包”类问题,记忆化搜索 对初学者 理解 动态规划 更友好,也能更好的感受到其魅力所在。

记忆化搜索,所谓 “记忆” 引用 Geeksforgeeks 网站上介绍记忆搜索原文中一句话就是 “to transform the results of a function into something to remember.” 把函数的结果存储下来作为 “记忆”。将“记忆”应用于搜索算法上,也就是搜索到有记录了函数结果的地方,其实就不需要再进行函数计算,直接返回 “记忆” 的结果即可。

记忆化搜索是一种自顶向下(Top-Down)分析的算法,文字描述过于悬浮于理论,保持本系列文风且用算法题来看下记忆化搜索算法具体的内容。

自顶向下(Top-Down)

LeetCode 329. 矩阵中的最长递增路径【困难】

给定一个 m x n 整数矩阵 matrix ,找出其中 最长递增路径 的长度。
对于每个单元格,你可以往上,下,左,右四个方向移动。 你 不能 在 对角线 方向上移动或移动到 边界外。(即不允许环绕)。

请在此添加图片描述

  • 直接顺着题意进行分析,找到 “最长递增路径” 直接用搜索遍历,把 matrix每个单元格的最长递增路径都计算下,返回其中最长的路径。不妨就假设下有一个 search的函数能够计算出 以当前单元格为起点的最长递增路径长度。遍历过程中 的最大长度 存储再 max 中,最后返回即可,代码如下:
// 假设中的函数
public int search(int[][] matrix,int x,int y){
	int max;
	return max;
}

public int longestIncreasingPath(int[][] matrix) {

        int max = 0;
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
            	//(i,j)格的最长递增路径长度 
                max = Math.max(max,search(matrix,i,j));
            }
        }
        return max;
    }
  • 问题开始关键点现在变成了 search 函数的实现;接着顺着题意分析,可以往上,下,左,右四个方向移动.首先考虑一个方向情况,只往上方向移动且可以往上移动(上方相邻的单元格大于当前单元格,递增),那么此时 当前单元格的最大路径长度就是 上方单元格的最大路径 + 1,其中1代表当前单元格。
public int search(int[][] matrix,int x,int y){
    
	int number = matrix[x][y],up = 1;

	// 保障可以往“上”移动, x-1 没有越边界( x > 0 )
	// 且是 递增的 matrix[x-1][y] > number
    if( x > 0 && matrix[x-1][y] > number ){

    	 // 递归调用,同类子问题,(x-1,y)格的最长递增路径长度 
         up += search( matrix, x-1, y );
    }

	return up;
}
  • 如此,扩展到 四个方向,最终当前单元格最大路径就是 四个方向中取最大的返回。
public int search(int[][] matrix,int x,int y){

        int number = matrix[x][y],up = 1,down =1,left=1,right=1;

        if(x>0 && matrix[x-1][y] > number){
            up += search(matrix,x-1,y);
        }
        if(x+1<matrix.length && matrix[x+1][y] > number){
            down += search(matrix,x+1,y);
        }
        if(y+1<matrix[0].length && matrix[x][y+1] > number){
            right += search(matrix,x,y+1);
        }
        if(y>0 && matrix[x][y-1] > number){
            left += search(matrix,x,y-1);
        }

        return Math.max(Math.max(up,down),Math.max(right,left));
}
  • 实现到这里按照搜索思路的算法已经完成;不过会发现性能不高,分析过程会发现调用 search函数 时候,同样一格位置会计算多次。此时联系想想先前提到的 “记忆” ,把函数的结果存储下来作为 “记忆”,也就是用 一个二维数组 cahce 缓存起来 已经计算过单元的结果。

    search 实现改为 (cache需要在 longestIncreasingPath 根据 matrx大小 new下,这里略) ,代码如下:

public int[][] cache = null;

public int search(int[][] matrix,int x,int y){

        if(0 != cache[x][y])
            return cache[x][y];

        int number = matrix[x][y],up = 1,down =1,left=1,right=1;
        if(x>0 && matrix[x-1][y] > number){
            up += search(matrix,x-1,y);
        }
        if(x+1<matrix.length && matrix[x+1][y] > number){
            down += search(matrix,x+1,y);
        }
        if(y+1<matrix[0].length && matrix[x][y+1] > number){
            right += search(matrix,x,y+1);
        }
        if(y>0 && matrix[x][y-1] > number){
            left += search(matrix,x,y-1);
        }
        // 存储 “记忆”
        cache[x][y] = Math.max(Math.max(up,down),Math.max(right,left));
        return cache[x][y];
}
  • 到此,已经按照记忆化搜索算法思路完成了问题的解决。

回顾下记忆化搜索解题过程,我们是从算法问题出发 -> 分析需要完成的计算(子问题)-> 进一步进行解决。这其实就是 自顶向下(Top-Down)的思考方式。

记忆化搜索 与 动态规划

再来看"记忆",cache[x][y] 所记录的是 x,y 这个单元格已经计算过的 最大路径长度当前单元格 的最大路径长度使用上、下、左、右四个方向上的单元格 最大路径长度 来进行计算 ,使用"记忆" 其实就是在使用子问题的最优解。

	
	current_max = Max( up+1, down+1, left+1, right+1 )

另外一个角度描述,规划决策 当前单元格的最大路径 是根据 (上、下、左、右)相邻四个方向上的单元格最大路径 进行的计算。相邻四个方向上的最大路径(子问题最优解) 并非一开始静态写入下来,而是在程序运行过程中至少计算一次存储下来,可看作 动态的计算。根据动态计算子问题最优结果来进行规划决策当前最优结果,也就是所谓 动态规划(Dynamic Programming)的字面意思。

可以多体会下: 解决最优化问题,根据子问题的最优结果 规划决策 -> 当前问题最优结果 -> 进而求解最初问题。

所以有一种说法就是:记忆化搜索 是 动态规划 自顶向下(Top-Down)分析的一种实现形式,通常用递归来实现。

最后总结本文

  1. 本系列文章中写此篇 承前启后的思考,记忆化搜索 的基本概念;
  2. 通过一道题演示 自顶向下(Top-Down)的分析,实际应用记忆化搜索解决 具体算法问题;
  3. 解读 记忆化搜索 与 动态规划 的关系,以及动态规划一些概念;

下一篇咱们再一起继续解读 动态规划(Dynamic Programming) ,欢迎关注 Java研究者专栏、博客、公众号等

标签:search,matrix,Memorize,int,max,单元格,Search,记忆,数据结构
From: https://www.cnblogs.com/jzhlin/p/Memorize_Search.html

相关文章

  • 历时三年,写的一本数据结构与算法pdf,开源了!
    前言大家好,我是bigsai,很早就在写博客,将文章整理成了一个pdf,并且开源到github上!自己写东西断断续续也不少时间了,也写了不少东西(虽然是偏向小白),这个其实花费的时间还是比较多的,这次的话主要将数据结构与算法中一些文章整理出来,初步整理成一版pdf,先分享给大家。因为在整理pdf方......
  • 数据结构之树(树转化为二叉树也叫二叉化)
    说明对于将一般树结构转化为二叉树,使用的方法称为“Child-Sibling”(Leftmost-child-next-right-sibling)法则。步骤1.将节点的所有兄弟节点,用横线连接起来2.删掉所有与子节点间的链接,只保留与最左子节点的链接3.顺时针旋转45度 二叉树转化为多叉树与树转化为二叉树......
  • 数据结构之树(线索树)
    线索二叉树二叉树有些节点没有左子树或没有右子树或左右子树都没有,那么就会存在空链接的情况,为了充分利用空链接,让其指向树的其他节点,这些指向其他节点的链接就是线索,这棵树也变成了线索二叉树。二叉树变成线索二叉树的步骤1.二叉树先根据中序遍历的方式,进行排序(这样节点就直......
  • Redission实现公平锁为什么要使用ZSet数据结构?
    Redission实现公平锁为什么要使用ZSet数据结构?使用ZSet结构有什么好处?看lua代码好像也并没有使用到ZSet的二分查找这种优势,在Redisson中实现公平锁时使用ZSet(有序集合)数据结构有以下几个好处:具有排序功能:ZSet是有序的数据结构,其中的每个元素都有一个分数(score)与之相关联。这使得R......
  • JavaSEday05 泛型,数据结构,List,Set集合
    javSEday05泛型,数据结构,List,Set今日目标泛型使用数据结构ListSet1泛型1.1泛型的介绍泛型是一种类型参数,专门用来保存类型用的最早接触泛型是在ArrayList,这个E就是所谓的泛型了。使用ArrayList时,只要给E指定某一个类型,里面所有用到泛型的地方都会被......
  • JavaSE day05【泛型,数据结构,List接口,Set接口】测评题
    选择题题目1(单选):查看下列代码,选出正确的传参()publicclassTest2{publicstaticvoidmain(String[]args){ArrayList<Integer>list1=newArrayList<Integer>();ArrayList<Number>list2=newArrayList<Number>();Arr......
  • 一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)
    请问以下JS代码的执行结果是什么?functioncontrol(x){if(x==3)thrownewError("break");}functionfoo(x=6){return{next:()=>{control(x);return{done:!x,value:x&&x--};}}}letx=newObject;x[Symbol.......
  • 数据结构之树(二叉排序树)
    特点二叉排序树(BinarySearchTree,BST)的特点:每个节点最多有两个子节点,分别称为左子节点和右子节点。节点的左子树中的所有节点的值都小于该节点的值。节点的右子树中的所有节点的值都大于该节点的值。左子树和右子树也分别是二叉排序树。BST的主要优点是可以实现高效的查......
  • python3: dlt - 数据结构2
    python3:dlt-数据结构2    一、源程序1[wit@fedoranull]$cattest.py2#!/usr/bin/envpython334567#file_name=test.py8#python_verion=3.11.1910111213#testthisscript14defmsg():15print......
  • python3: dlt - 数据结构
    python3:dlt-数据结构    一、程序:1[wit@fedoranull]$cattest.py2#!/usr/bin/envpython334567#testthisscript8defmsg():9print("\nhello,python3!\n")101112#runningmsg()13#msg()1415......