目录
不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
- 输入:m = 3, n = 7
- 输出:28
示例 2:
- 输入:m = 2, n = 3
- 输出:3
解释: 从左上角开始,总共有 3 条路径可以到达右下角。
- 向右 -> 向右 -> 向下
- 向右 -> 向下 -> 向右
- 向下 -> 向右 -> 向右
示例 3:
- 输入:m = 7, n = 3
- 输出:28
示例 4:
- 输入:m = 3, n = 3
- 输出:6
提示:
- 1 <= m, n <= 100
- 题目数据保证答案小于等于 2 * 10^9
回溯算法思路
创建
Solution
类并定义uniquePaths
方法,该方法接收矩阵的行数m
和列数n
,返回不同路径的数量。在
uniquePaths
方法中,创建一个大小为m*n
的矩阵grid
用于表示走过的路径,创建一个大小为m*n
的visited
数组用于表示当前位置是否已经被访问过。调用
backtrack
方法开始回溯搜索不同的路径,并返回搜索到的路径数量。在
backtrack
方法中,判断当前位置是否为目标位置,如果是,则找到一条新的路径,路径数量加1。判断当前位置是否越界或已经被访问过,如果是,则不能继续走,返回到上一步。
标记当前位置已经被访问,向右和向下分别搜索一步,并记录不同路径的数量。
撤销当前选择,回溯到上一步。
在
main
方法中,创建一个Solution
实例,传入矩阵的行数和列数,调用uniquePaths
方法并输出不同路径的数量。
package com.company;
class Solution {
public int uniquePaths(int m, int n) {
int[][] grid = new int[m][n];
boolean[][] visited = new boolean[m][n];
backtrack(grid, visited, 0, 0, m - 1, n - 1);
return count;
}
public static int count = 0; // 用于记录不同路径数量
private void backtrack(int[][] grid, boolean[][] visited, int row, int col, int m, int n) {
if (row == m && col == n) { // 当前位置为目标位置
count++; // 找到一条新的路径,路径数量加1
return;
}
if (row > m || col > n || visited[row][col]) { // 越界或当前位置已经被访问过,不能继续走
return;
}
visited[row][col] = true; // 标记当前位置已经被访问
backtrack(grid, visited, row, col + 1, m, n); // 向右走一步
backtrack(grid, visited, row + 1, col, m, n); // 向下走一步
visited[row][col] = false; // 撤销当前选择,回溯到上一步
}
public static void main(String[] args) {
Solution solution = new Solution();
int m = 3;
int n = 3;
int paths = solution.uniquePaths(m, n);
System.out.println("The number of unique paths from top-left to bottom-right is: " + paths);
}
}
visited[row][col] = true; // 标记当前位置已经被访问
visited[row][col] = false; // 撤销当前选择,回溯到上一步
可以删去,因为只能向下走或者向右走,又不能回头
时间复杂度很高,大概率超时!
动态规划思路
- 确定dp数组下标含义:dp[i][j]表示从起点到坐标(i,j)的不同路径数目。
- 递推公式:dp[i][j] = dp[i-1][j] + dp[i][j-1]。因为每次只能往右或者往下走,所以到达(i,j)这个位置的路径数目,等于到达(i-1,j)和到达(i,j-1)的路径数目之和。
- 初始化:第一行和第一列上的位置只能沿着一条直线走,所以在这里的路径数目都是1。
- 遍历顺序:可以按行或按列遍历,这里按行遍历,从第2行开始到第m行,对于每一行从第2列开始到第n列。
- 推导结果:最后返回dp[m-1][n-1],即到达终点的路径数目。
package com.company;
public class Solution {
/**
* 1. 确定dp数组下标含义 dp[i][j] 到每一个坐标可能的路径种类
* 2. 递推公式 dp[i][j] = dp[i-1][j] dp[i][j-1]
* 3. 初始化 dp[i][0]=1 dp[0][i]=1 初始化横竖就可
* 4. 遍历顺序 一行一行遍历
* 5. 推导结果 。。。。。。。。
*
* @param m 行数
* @param n 列数
* @return 到达右下角的所有路径数量
*/
public static int uniquePaths(int m, int n) {
int[][] dp = new int[m][n]; // 定义 dp 数组,记录到每个坐标的可能路径数
// 初始化 dp 数组,因为对于第一行和第一列,只有一种可能的路径数,即沿着边走
for (int i = 0; i < m; i++) {
dp[i][0] = 1;
}
for (int i = 0; i < n; i++) {
dp[0][i] = 1;
}
// 逐行计算 dp 数组中的值,从第二行和第二列开始
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = dp[i-1][j] + dp[i][j-1]; // 状态转移方程,到达当前坐标的可能路径数为从上方和左方的路径数之和
}
}
return dp[m-1][n-1]; // 返回到达右下角的可能路径数
}
public static void main(String[] args) {
int m = 3;
int n = 3;
int paths = uniquePaths(m, n);
System.out.println("The number of unique paths from top-left to bottom-right is: " + paths);
}
}