小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
题目
一个机器人位于一个 m x n网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
**输入:**m = 3, n = 7 **输出:**28
示例 2:
**输入:**m = 3, n = 2 **输出:**3 解释: 从左上角开始,总共有 3 条路径可以到达右下角。
- 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右
- 向下 -> 向右 -> 向下
示例 3:
**输入:**m = 7, n = 3 **输出:**28
示例 4:
**输入:**m = 3, n = 3 **输出:**6
提示:
1 <= m, n <= 100- 题目数据保证答案小于等于
2 * 109
递归 · 深度优先搜索
解题思路
- 从
[0, 0]到[m - 1, n - 1],每次i + 1或j + 1。直到越界或到达,到达路线数+1
代码
var uniquePaths = function(m, n, i = 0, j = 0) {
return i === m - 1 && j === n - 1 ? 1 : i === m || j === n ? 0 : uniquePaths(m, n, i + 1, j) + uniquePaths(m, n, i, j + 1)
};
结果
测试用例19, 13超时
递归 · 记忆化
解题思路
- 以递归函数变化的
形参ij为键名构造缓存函数。哈希表记忆已到过节点后面路线数
代码
var uniquePaths = function(m, n, i = 0, j = 0, h = new Map, t, t1) {
return i === m - 1 && j === n - 1 ? 1 : i === m || j === n ? 0 : h.has(t = i * 100 + j) ? h.get(t) : (h.set(t, (t1 = uniquePaths(m, n, i + 1, j, h) + uniquePaths(m, n, i, j + 1, h))), t1)
};
结果
动态规划
解题思路
dp[i][j]到达点[i, j]路径数,dp[i][j] = dp[i - 1][j] + dp[i][j - 1]- 已知到
i === 0首行,j === 0首列上的点路径数都为1
代码
初始赋值
var uniquePaths = function(m, n, dp = Array.from({length: m}, _ => new Uint32Array(n))) {
for(var i = 0; i < m; i++) dp[i][0] = 1
for(var j = 0; j < n; j++) dp[0][j] = 1
for(var i = 1; i < m; i++)
for(var j = 1; j < n; j++)
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
return dp[m - 1][n - 1]
};
3元判断
var uniquePaths = function(m, n, dp = Array.from({length: m}, _ => new Uint32Array(n))) {
for(var i = 0; i < m; i++)
for(var j = 0; j < n; j++)
dp[i][j] = i === 0 || j === 0 ? 1 : dp[i - 1][j] + dp[i][j - 1]
return dp[m - 1][n - 1]
};
结果
动态规划 · 降维
解题思路
- 遍历行,
dp[j]到列j路径数。扫描列:本行dp[j]= 上行dp[j]↓ + 左列dp[j - 1]→ - 遍历列,
dp[i]到行i路径数。扫描行:本列dp[i]= 左列dp[i]→ + 上行dp[i - 1]↓
代码
遍历行,扫描列,直到第m \- 1行的第n \- 1列,即点[m \- 1, n \- 1]
var uniquePaths = function(m, n, dp = new Uint32Array(n).fill(1)) {
for(var i = 1; i < m; i++) for(var j = 1; j < n; j++) dp[j] = dp[j] + dp[j-1]
return dp[n - 1] // 即 dp[dp.length - 1]
};
var uniquePaths = function(m, n, dp = new Uint32Array(n).fill(1), i = 0) {
while(j = 0, ++i < m) while(++j < n) dp[j] += dp[j - 1]
return dp[n - 1]
};
遍历列,扫描行,直到第n \- 1列的第m \- 1行,即点[m \- 1, n \- 1]
var uniquePaths = function(m, n, dp = new Uint32Array(m).fill(1)) {
for(var j = 1; j < n; j++) for(var i = 1; i < m; i++) dp[i] = dp[i] + dp[i-1]
return dp[m - 1] // 即 dp[dp.length - 1]
};`
`var uniquePaths = function(m, n, dp = new Uint32Array(m).fill(1), j = 0) {
while(i = 0, ++j < n) while(++i < m) dp[i] += dp[i - 1]
return dp[m - 1]
};
结果
组合
解题思路
- 从
[0, 0]走到[m, n],只能向右或向下,总步数m - 1 + n - 1 = m + n - 2步 向右走m - 1步时,一定向下走n - 1步。从总步数选m - 1或n - 1种组合都行- 以选
m - 1为例:- C(m + n - 2, m - 1) = (m + n - 2)! / (m - 1)! * (n - 1)! =
(m + n - 2) * (m + n - 1) ... m * (m - 1 * m - 2 ... 1)
—————————————————————— =
(m - 1 * m - 2 ... 1) * (n - 1)!
(m + n - 2) * (m + n - 1) ... m
—————————————————————— =
(n - 1)!
m起,每次加1 到 m + n - 2,累乘n - 1次
—————————————————————— =
1起,每次加1 到 n - 1, 累乘n - 1次
- C(m + n - 2, m - 1) = (m + n - 2)! / (m - 1)! * (n - 1)! =
代码
选m \- 1
var uniquePaths = function(m, n, r = 1, i = m, j = 1) {
while(j < n) r = r * i++ / j++ | 0
return r
};
选n \- 1
var uniquePaths = function(m, n, r = 1, i = n, j = 1) {
while(j < m) r = r * i++ / j++ | 0
return r
};