(LeetCodeHot100)62. 不同路径——unique-paths
(LeetCodeHot100)62. 不同路径——unique-paths
zhangzhang62. 不同路径——unique-paths
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
1 | 输入:m = 3, n = 7 |
示例 2:
1 | 输入:m = 3, n = 2 |
示例 3:
1 | 输入:m = 7, n = 3 |
示例 4:
1 | 输入:m = 3, n = 3 |
提示:
1 <= m, n <= 100- 题目数据保证答案小于等于
2 * 109
方法一:动态规划
思路与算法
我们用 f(i,j) 表示从左上角走到 (i,j) 的路径数量,其中 i 和 j 的范围分别是 [0,m) 和 [0,n)。
由于我们每一步只能从向下或者向右移动一步,因此要想走到 (i,j),如果向下走一步,那么会从 (i−1,j) 走过来;如果向右走一步,那么会从 (i,j−1) 走过来。因此我们可以写出动态规划转移方程:
f(i,j)=f(i−1,j)+f(i,j−1)
需要注意的是,如果 i=0,那么 f(i−1,j) 并不是一个满足要求的状态,我们需要忽略这一项;同理,如果 j=0,那么 f(i,j−1) 并不是一个满足要求的状态,我们需要忽略这一项。
初始条件为 f(0,0)=1,即从左上角走到左上角有一种方法。
最终的答案即为 f(m−1,n−1)。
细节
为了方便代码编写,我们可以将所有的 f(0,j) 以及 f(i,0) 都设置为边界条件,它们的值均为 1。
代码
1 | class Solution { |
此外,由于 f(i,j) 仅与第 i 行和第 i−1 行的状态有关,因此我们可以使用滚动数组代替代码中的二维数组,使空间复杂度降低为 O(n)。( “当前状态只依赖上一行” 这个本质 —— 滚动数组通过「覆盖旧值」的方式,用一维数组复用空间,f[j] 在更新前恰好保存着上一行的对应值)
因为一维数组的更新顺序是 从左到右(j从1到n-1),且我们用「覆盖旧值」的方式复用空间:
- 在计算第
i行的j列之前,f[j]还没被修改,保存的是第i-1行j列的值(即f[i-1][j],“上方” 的路径数); - 计算时,
f[j-1]已经被修改过(因为j-1 < j,先遍历j-1),保存的是第i行j-1列的值(即f[i][j-1],“左方” 的路径数); - 执行
f[j] += f[j-1]后,f[j]就从 “上一行的值” 变成了 “当前行的值”,供下一列(j+1)使用。
1 | class Solution { |
复杂度分析
- 时间复杂度:O(mn)。
- 空间复杂度:O(min(m,n)),即为存储所有状态需要的空间。注意到 f(i,j) 仅与第 i 行和第 i−1 行的状态有关,因此我们可以使用滚动数组代替代码中的二维数组,使空间复杂度降低为 O(n)。此外,由于我们交换行列的值并不会对答案产生影响,因此我们总可以通过交换 m 和 n 使得 m≤n,这样空间复杂度降低至 O(min(m,n))。
方法二:组合数学
思路与算法
从左上角到右下角的过程中,我们需要移动 m+n−2 次,其中有 m−1 次向下移动,n−1 次向右移动。因此路径的总数,就等于从 m+n−2 次移动中选择 m−1 次向下移动的方案数,即组合数:
$\begin{equation} C_{m+n-2}^{m-1} = \binom{m+n-2}{m-1} = \frac{(m + n - 2)(m + n - 3) \cdots n}{(m - 1)!} = \frac{(m + n - 2)!}{(m - 1)!(n - 1)!} \end{equation}$
因此我们直接计算出这个组合数即可。计算的方法有很多种:
- 如果使用的语言有组合数计算的 API,我们可以调用 API 计算;
- 如果没有相应的 API,我们可以使用 (m−1)!(m+n−2)(m+n−3)⋯n 进行计算。
代码
1 | class Solution { |
复杂度分析
- 时间复杂度:O(m)。由于我们交换行列的值并不会对答案产生影响,因此我们总可以通过交换 m 和 n 使得 m≤n,这样时间复杂度降低至 O(min(m,n))。
- 空间复杂度:O(1)。



