JavaScript LeetCode Diary 4

124 阅读3分钟

2020-04-09 (Array, DP)

今天开始刷 dp 的栏目。感觉数组的还没搞得很畅快,所以打算挑也属于数组的题目做。今天做机器人路径的经典题。

每天三道 medium 感觉有点难顶,时间花的有点多。今天开始调整一下,2 medium 1 easy。 等感觉来了再换回原来的计划。

1025. Divisor Game

先找一道 easy 试试手。找到这题。和原来有一道 Nim Game(292) 的题目异曲同工。那道题是不能剩余不能等于4,因为对手拿任意1,2,3 个,都会留下最后一个给自己拿,就能保证胜利。这个题目也是,先手的人,只要是偶数个,都可以回到基态,也就是剩 2 个的时候,谁到 2 谁赢。如果是奇数个,不管怎么操作,都只能是对手遇到偶数个。

这题和 292 一样,点踩的远大于点赞的。我也不喜欢,胜负在还没开始前就已决定,有点宿命论的意味。

/**
 * @param {number} N
 * @return {boolean}
 */
var divisorGame = function (N) {
  return N % 2 === 0;
};

62. Unique Paths

一开始我是这么初始化这个二维数组的:

const dp = new Array(m).fill(new Array(n).fill(1));

结果一不小心踩了个坑。因为我在第一个 fill 中是通过 new 创建的 Array 对象。但是通过 fill 传入的对象一定要注意 reference 的问题。

// A single object, referenced by each slot of the array:
let arr = Array(3).fill({}) // [{}, {}, {}]
arr[0].hi = "hi"            // [{ hi: "hi" }, { hi: "hi" }, { hi: "hi" }]

过了这个坑的话,这题就很明朗了。每一个小正方形的右下角的走法,等于他上方过来加上左边过来的走法之和。

/**
 * @param {number} m
 * @param {number} n
 * @return {number}
 */
var uniquePaths = function (m, n) {
  // 构建一个 m*n 的二位数组,这个数组的每个位置,记录从出发点到改点的路径走法数目
  const dp = [];

  for (let i = 0; i < m; i++) {
    dp[i] = new Array(n).fill(1);

    for (let j = 0; j < n; j++) {
      if (i > 0 && j > 0) {
        // 每一个小正方形的右下角的走法,等于他上方过来加上左边过来的走法之和
        dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
      }
    }
  }
  return dp[m - 1][n - 1];
};

// uniquePaths(3, 2);
uniquePaths(7, 3);

63. Unique Paths II

这道题一开始想简单了。开始的想法是,遇到障碍,把那个点置为零,然后继续遍历。但是遇到一个情况,就是中间一个数组全是障碍,如果还用老方法加的话,会产生错误的结果,这种情况应该是没有路径的。然后还有两个场景,左上和右下两点都是障碍时,也没有办法走到。

第二个是这题可以不用二维数组去记录,用一个一维数组就够了。可以省一些空间。内层循环,当 j 等于 0 的时候,计算沿用的一直是最开始初始赋的 1,除非在之前被置为 0。

dp[j] = dp[j] + dp[j - 1] 右侧的 dp[j] 其实是上一行同样位置的值。

/**
 * @param {number[][]} obstacleGrid
 * @return {number}
 */
var uniquePathsWithObstacles = function (obstacleGrid) {
  const row = obstacleGrid.length;
  const col = obstacleGrid[0].length;

  if (obstacleGrid[0][0] === 1 || obstacleGrid[row - 1][col - 1] === 1) return 0;

  // 初始化 dp 数组,给一个初始值 1,一步不走的情况在上面已经 return。
  const dp = new Array(col).fill(0);
  dp[0] = 1;

  for (let i = 0; i < row; i++) {
    for (let j = 0; j < col; j++) {
      if (obstacleGrid[i][j] === 1) {
        dp[j] = 0;
      } else if (j > 0) {
        // 当 j 等于 0 的时候,计算沿用的一直是最开始初始赋的 1,除非在之前被置为 0
        // dp[j] = dp[j] + dp[j - 1] 右侧的 dp[j] 其实是上一行同样位置的值
        dp[j] = dp[j] + dp[j - 1];
      }
    }
  }
  return dp[col - 1];
};

uniquePathsWithObstacles([[0, 0, 0], [0, 1, 0], [0, 0, 0]]);
uniquePathsWithObstacles([[0, 1]]);
uniquePathsWithObstacles([[0, 0], [1, 0]]);
uniquePathsWithObstacles([[0, 0], [1, 1], [0, 0]]);