「前端刷题」62. 不同路径

83 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

题目

一个机器人位于一个 m x n网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

 

示例 1:

**输入:**m = 3, n = 7 **输出:**28

示例 2:

**输入:**m = 3, n = 2 **输出:**3 解释: 从左上角开始,总共有 3 条路径可以到达右下角。

  1. 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右
  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 + 1j + 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超时

递归 · 记忆化

解题思路

  • 以递归函数变化的形参 i j键名构造缓存函数哈希表记忆已到过节点后面路线数

代码

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 - 1n - 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次

代码

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
};

结果

组合解法运行结果