【记忆化搜索】不同路径

文章目录

题目链接:62. 不同路径


一、题目解析

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

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

回到题目,本题给出一个 m x n 网格。机器人的起始位置在网格的左上角(坐标 1, 1),并且每次只能向下或者向右移动一步,如果机器人要到达网格的右下角,总共有多少条不同的路径?我们需要返回路径条数。

例1:m = 3, n = 7

返回的路径条数是 28


DFS:深度优先搜索

算法原理

我们的目标是 找到机器人从起始位置【1, 1】到达终点【m, n】的路径数 ,并且根据题目的规则(机器人每次只能向下或者向右移动一步)可以知道:到达【m, n】这个位置的方式只有两个,一个是从该位置的上方(【m-1, n】)往下走一步,另一个则是从该位置的左方(【m, n-1】)往右走一步

要想知道到达【m-1, n】和【m, n-1】的路径数,也是一样:找出到达该位置的上方和左方的位置的路径数。这就是相同的问题,我们可以使用递归来解决。

dfs 函数

函数头

我们给 dfs 函数设置的任务是:找出到达【row, col】位置的路径数 ,其中 rowcol 是位置的坐标,作为参数,返回值为 int。

java 复制代码
int dfs(int row, int col);

函数体

由于本题起始位置的坐标是【1, 1】,因此递归出口需要判断 row 和 col 为 0 的情况、为 1 的情况:

  1. row 和 col 为 0:【0, 0】是非法的坐标,无法到达这个位置,即 0 条路径
  2. row 和 col 为 1:【1, 1】是起始位置,到达这个位置的路径数只有 1 条

然后便是递归展开。

代码实现

java 复制代码
class Solution {
    public int uniquePaths(int m, int n) {
        return dfs(m, n);
    }

    // dfs任务:返回到达[row,col]位置的路径数
    public int dfs (int row, int col) {
        // 非法情况(下标从1开始)
        if (row == 0 || col == 0) {
            return 0;
        }
        // 位于起始位置
        if (row == 1 && col == 1) {
            return 1;
        }
				
        // 递归展开
        return dfs(row-1, col) + dfs(row, col-1);
    }
}

记忆化搜索

使用递归的方法由于时间复杂度达到指数级别,超时无法通过。

当我们求 dfs(4, 4) 时,递归的部分展开图如下:

可以发现有很多的展开都是重复的,正是大量重复的递归展开导致超时,我们可以想办法通过剪枝来减少重复的递归展开操作,从而提高算法的效率。

算法原理

记忆化搜索的本质就是 "带着记忆递归搜索" ,而 记忆数组/备忘录 正是这类问题的关键。

本题有两个变量------ dfs 函数的两个参数,我们将这两个参数坐标所对应的位置的路径数记录在记忆数组/备忘录中,等到需要这个位置的路径数时,直接从记忆数组/备忘录中取即可,无需再次递归展开了。

记忆数组

本题的记忆数组是一个二维数组,值是到达位置【row, col】的路径数。本题无需对其做初始化操作,默认值为0。

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

代码实现

java 复制代码
class Solution {
    int[][] memo; // 记忆数组

    public int uniquePaths(int m, int n) {
        // 初始化
        memo = new int[m+1][n+1];

        return dfs(m, n);
    }

    // dfs任务:返回到达[row,col]位置的路径数
    public int dfs (int row, int col) {
        // 递归展开之前,先查看记忆数组
        if (memo[row][col] != 0) {
            return memo[row][col];
        }

        // 非法情况(下标从1开始)
        if (row == 0 || col == 0) {
            return 0;
        }
        // 位于起始位置
        if (row == 1 && col == 1) {
            memo[row][col] = 1;
            return 1;
        }

        // 递归展开之后先将结果存入记忆数组
        memo[row][col] = dfs(row-1, col) + dfs(row, col-1);

        return memo[row][col];
    }
}

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

相关推荐
Sam_Deep_Thinking1 小时前
new Thread()之后发生了什么?
java·后端·面试·程序员
泡海椒1 小时前
规则引擎对比:JQuick-Java vs Drools 轻量场景选型对比
java·开发语言
wno7041 小时前
Spring Security短信验证码登录
java·python·spring
步行cgn1 小时前
Spring util 命名空间详解
java·后端·spring
古法安卓2 小时前
Android-休眠唤醒后onLocationChanged没有数据问题排查
android·java·android studio
怕浪猫2 小时前
从 Windows 换到 Mac 三个月,我真香了
算法·面试·架构
周GZ2 小时前
简单讲解线程池的4种拒绝策略
java
白远山2 小时前
24小时自助健身房系统开发实战:从需求分析到完整指南
java·开发语言·数据库·数据挖掘·需求分析
暮雨哀尘3 小时前
JAVA基础练习(四):SpringBoot 实现简易登录功能
java·spring boot·eclipse·maven