文章目录
题目链接: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】位置的路径数 ,其中 row 和 col 是位置的坐标,作为参数,返回值为 int。
java
int dfs(int row, int col);
函数体
由于本题起始位置的坐标是【1, 1】,因此递归出口需要判断 row 和 col 为 0 的情况、为 1 的情况:
- row 和 col 为 0:【0, 0】是非法的坐标,无法到达这个位置,即 0 条路径
- 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。
- 在递归展开之前,先查看记忆数组/备忘录
- 在递归展开之后,先将得到的结果存入记忆数组/备忘录,再返回
代码实现
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];
}
}
文章到这里就告一段落啦,若有错误请尽管指出~
完