不同路径 图解题解
这道题到底在问什么
- 输入
- m=4, n=5
- 输出
- 35
最优解:为什么这么做
一句话答案:LeetCode 62 不同路径的标准解法是二维动态规划:机器人只能向右或向下走,到达任意格子只可能来自正上方或正左方,故 dp[i][j] = dp[i-1][j] + dp[i][j-1],其中 dp[i][j] 是走到 (i,j) 的路径数,第一行第一列全为 1。时间 O(m×n),空间可压缩到 O(n)。
不同路径这道题在问什么
一个 m 行 n 列的网格,机器人从左上角 (0,0) 出发,每一步只能向右或向下移动一格,问走到右下角 (m-1,n-1) 共有多少条不同的路径。要求的是路径条数而不是最短路——每条合法路径长度其实都一样(恰好右移 n-1 次、下移 m-1 次),区别只在这些移动的先后顺序。m=4、n=5 时答案是 35。
为什么想到用动态规划数路径
直接搜索是可行的起点:从起点出发递归枚举每一步向右还是向下,走到终点计一条。但路径条数随网格尺寸指数级增长,纯递归会把同一个格子的子问题重复算无数遍——走到 (2,2) 的方式有很多种,可从 (2,2) 到终点的路径数只需要算一次。
关键观察是反过来看:由于只能向右或向下,任何一条到达 (i,j) 的路径,最后一步只有两种可能——从正上方 (i-1,j) 下来,或从正左方 (i,j-1) 过来,不存在第三个入口。这两类路径最后一步不同,必然互不重复;而所有路径又必属其一,不会遗漏。于是「到 (i,j) 的路径数 = 到上方的路径数 + 到左方的路径数」,加法原理保证不重不漏,问题被拆成规模更小的同类问题。
dp 数组怎么定义,第一行第一列为什么全是 1
定义 dp[i][j] 为从起点 (0,0) 走到 (i,j) 的不同路径条数,答案就是 dp[m-1][n-1]。边界要先铺好:第一行的格子上方没有格子,只能从左边一路向右走过来,路径只有一条;同理第一列只能一路向下。所以第一行、第一列全部初始化为 1,起点 dp[0][0]=1 也读作「原地不动算一种走法」。
这个初始化是整张表的地基。忘了铺 1、留成 0,后面每一格都是在 0 上做加法,整张表全错——这是本题最常见的错误。
转移方程为什么成立,从哪一格开始填
内部格子套 dp[i][j] = dp[i-1][j] + dp[i][j-1]。它成立的依据就是上面的两入口观察:两个来源的路径集合互斥且完备,条数直接相加。填表顺序按行从上到下、每行从左到右,保证算任何一格时它的上方和左方已经算好。
主循环要从 (1,1) 开始而不是 (0,0):边界已经填好了,从 (1,1) 起每一格才同时有「上」和「左」可加。以样例收尾验证一下,右下角累加出 dp[3][4]=35,与答案一致。
复杂度多少,空间怎么压到一行
时间复杂度 O(m×n):每个格子只被计算一次,每次是一次加法。空间 O(m×n) 存整张表;由于每格只依赖上一行同列和本行左邻,可以用一维数组滚动更新 dp[j] += dp[j-1],空间降到 O(n)。
一个常见的延伸问法:如果网格里有障碍物(LeetCode 63 不同路径 II),框架不变,把障碍格的 dp 直接置 0 表示走不到,其余格照样上加左;若起点本身是障碍,答案直接为 0。
▶ 动画逐步走查(共 34 步)——想跟着动画一帧帧对照就展开
- 3记住这句「上面来 + 左边来」,下面每一格都在套它。
- 4起点 (0,0):原地不动也算一种走法,dp[0][0] = 1。
- 5第一行 (0,1):上面没有格子,只能从左边一路向右走过来,dp[0][1] = 1。
- 6第一行 (0,2):上面没有格子,只能从左边一路向右走过来,dp[0][2] = 1。
- 7第一行 (0,3):上面没有格子,只能从左边一路向右走过来,dp[0][3] = 1。
- 8第一行 (0,4):上面没有格子,只能从左边一路向右走过来,dp[0][4] = 1。
- 9第一列 (1,0):左边没有格子,只能从上面一路向下走过来,dp[1][0] = 1。
- 10算 (1,1):正上方 dp[0][1]=1(蓝),正左方 dp[1][0]=1(蓝),两条路汇到这里。
- 11相加:1 + 1 = 2,填进 (1,1)。从上面来的 1 条、从左边来的 1 条,合起来就这么多。
- 12算 (1,2):正上方 dp[0][2]=1(蓝),正左方 dp[1][1]=2(蓝),两条路汇到这里。
- 13相加:1 + 2 = 3,填进 (1,2)。从上面来的 1 条、从左边来的 2 条,合起来就这么多。
- 14算 (1,3):正上方 dp[0][3]=1(蓝),正左方 dp[1][2]=3(蓝),两条路汇到这里。
- 15相加:1 + 3 = 4,填进 (1,3)。从上面来的 1 条、从左边来的 3 条,合起来就这么多。
- 16算 (1,4):正上方 dp[0][4]=1(蓝),正左方 dp[1][3]=4(蓝),两条路汇到这里。
- 17相加:1 + 4 = 5,填进 (1,4)。从上面来的 1 条、从左边来的 4 条,合起来就这么多。
- 18第一列 (2,0):左边没有格子,只能从上面一路向下走过来,dp[2][0] = 1。
- 19算 (2,1):正上方 dp[1][1]=2(蓝),正左方 dp[2][0]=1(蓝),两条路汇到这里。
- 20相加:2 + 1 = 3,填进 (2,1)。从上面来的 2 条、从左边来的 1 条,合起来就这么多。
- 21算 (2,2):正上方 dp[1][2]=3(蓝),正左方 dp[2][1]=3(蓝),两条路汇到这里。
- 22相加:3 + 3 = 6,填进 (2,2)。从上面来的 3 条、从左边来的 3 条,合起来就这么多。
- 23算 (2,3):正上方 dp[1][3]=4(蓝),正左方 dp[2][2]=6(蓝),两条路汇到这里。
- 24相加:4 + 6 = 10,填进 (2,3)。从上面来的 4 条、从左边来的 6 条,合起来就这么多。
- 25算 (2,4):正上方 dp[1][4]=5(蓝),正左方 dp[2][3]=10(蓝),两条路汇到这里。
- 26相加:5 + 10 = 15,填进 (2,4)。从上面来的 5 条、从左边来的 10 条,合起来就这么多。
- 27第一列 (3,0):左边没有格子,只能从上面一路向下走过来,dp[3][0] = 1。
- 28算 (3,1):正上方 dp[2][1]=3(蓝),正左方 dp[3][0]=1(蓝),两条路汇到这里。
- 29相加:3 + 1 = 4,填进 (3,1)。从上面来的 3 条、从左边来的 1 条,合起来就这么多。
- 30算 (3,2):正上方 dp[2][2]=6(蓝),正左方 dp[3][1]=4(蓝),两条路汇到这里。
- 31相加:6 + 4 = 10,填进 (3,2)。从上面来的 6 条、从左边来的 4 条,合起来就这么多。
- 32算 (3,3):正上方 dp[2][3]=10(蓝),正左方 dp[3][2]=10(蓝),两条路汇到这里。
- 33相加:10 + 10 = 20,填进 (3,3)。从上面来的 10 条、从左边来的 10 条,合起来就这么多。
- 34算 (3,4):正上方 dp[2][4]=15(蓝),正左方 dp[3][3]=20(蓝),两条路汇到这里。
- 35相加:15 + 20 = 35,填进 (3,4)。从上面来的 15 条、从左边来的 20 条,合起来就这么多。
- 36表填满了。右下角 dp[3][4] = 35,就是机器人从左上走到右下的不同路径总数。
⚠️ 容易写错的地方
✗ 错:第一行/列忘了初始化为 1
✓ 对:边界必须先全填 1
它们没有「上」或「左」可加,留 0 会让整表全错
✗ 错:主循环从 (0,0) 开始
✓ 对:从 (1,1) 开始
边界已填好,从 1 起才有上和左可加
✗ 错:答案取错格
✓ 对:答案是 dp[m-1][n-1]
终点是右下角那一格
完整代码(Python / C++ / Java)
Python
def uniquePaths(m, n):
dp = [[1]*n for _ in range(m)]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[m-1][n-1]C++
int uniquePaths(int m, int n){
vector<vector<int>> dp(m, vector<int>(n, 1));
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];Java
int uniquePaths(int m, int n){
int[][] dp = new int[m][n];
for(int i=0;i<m;i++) dp[i][0] = 1;
for(int j=0;j<n;j++) dp[0][j] = 1;
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];复杂度
时间
O(m×n)
每格只算一次
空间
O(m×n)
存了整张表(可压成一行 O(n))
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 不同路径 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
如果网格里有障碍物(LC63)怎么改?+
障碍格的 dp 直接置 0(走不到),其余照样上+左相加;起点是障碍则答案 0。
能优化到 O(n) 空间吗?+
能。每格只依赖上一行同列和本行左边,用一维 dp 滚动更新 dp[j] += dp[j-1] 即可。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 不同路径 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。