[路飞]_前端算法第一二三弹-二进制矩阵中的最短路径

88 阅读3分钟

「这是我参与2022首次更文挑战的第30天,活动详情查看:2022首次更文挑战

给你一个 n x n 的二进制矩阵 grid 中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1

二进制矩阵中的 畅通路径 是一条从 左上角 单元格(即,(0, 0))到 右下角 单元格(即,(n - 1, n - 1))的路径,该路径同时满足下述要求:

  • 路径途经的所有单元格都的值都是 0
  • 路径中路径中所有相邻的单元格应当在 8 个方向之一 上连通(即,相邻两单元之间彼此不同且共享一条边或者一个角)。

畅通路径的长度 是该路径途经的单元格总数。

示例 1:

图片.png

输入:grid = [[0,1],[1,0]]
输出:2

示例 2:

图片.png

输入:grid = [[0,0,0],[1,1,0],[1,1,0]]
输出:4

示例 3:

输入:grid = [[1,0,0],[1,1,0],[1,1,0]]
输出:-1

典型的BFS最短路径问题,用DFS也可以求解,但是容易超时。

在二维矩阵中搜索,什么时候用BFS,什么时候用DFS?

1.如果只是要找到某一个结果是否存在,那么DFS会更高效。因为DFS会首先把一种可能的情况尝试到底,才会回溯去尝试下一种情况,只要找到一种情况,就可以返回了。但是BFS必须所有可能的情况同时尝试,在找到一种满足条件的结果的同时,也尝试了很多不必要的路径;

2.如果是要找所有可能结果中最短的,那么BFS回更高效。因为DFS是一种一种的尝试,在把所有可能情况尝试完之前,无法确定哪个是最短,所以DFS必须把所有情况都找一遍,才能确定最终答案(DFS的优化就是剪枝,不剪枝很容易超时)。而BFS从一开始就是尝试所有情况,所以只要找到第一个达到的那个点,那就是最短的路径,可以直接返回了,其他情况都可以省略了,所以这种情况下,BFS更高效。

BFS解法中的visited为什么可以全局使用?

BFS是在尝试所有的可能路径,哪个最快到达终点,哪个就是最短。那么每一条路径走过的路不同,visited(也就是这条路径上走过的点)也应该不同,那么为什么visited可以全局使用呢? 因为我们要找的是最短路径,那么如果在此之前某个点已经在visited中,也就是说有其他路径在小于或等于当前步数的情况下,到达过这个点,证明到达这个点的最短路径已经被找到。那么显然这个点没必要再尝试了,因为即便去尝试了,最终的结果也不会是最短路径了,所以直接放弃这个点即可。

 */
var shortestPathBinaryMatrix = function (grid) {
  let depth = 0;
  // 是一个正方形
  let m = grid.length - 1;
  // 边界情况,如果开头或结尾是1的情况下,不论怎么走都不可能达到题目要求
  if (grid[0][0] == 1 || grid[m][m] == 1) return -1;
  // 如果只有一个元素的情况
  if (m == 0) return 1;
  let queue = [[0, 0]];
  // 走过的路都标记为1  因为在BFS里面也只会去走为0的路
  grid[0][0] = 1;
  let directions = [
    // 垂直向上
    [0, -1],
    // 垂直向下
    [0, 1],
    // 向左
    [-1, 0],
    // 向右
    [1, 0],
    // 左上
    [-1, -1],
    // 左下
    [-1, 1],
    // 右上
    [1, -1],
    // 右下
    [1, 1],
  ];
  while (queue.length) {
    depth++;
    let size = queue.length;
    // 这样写比较简单
    while (size--) {
      let [x, y] = queue.shift();
      // 走到了终点
      if (x == m && y == m) return depth;
      for (let dir of directions) {
        let curX = x + dir[0],
          curY = y + dir[1];
        // 超出边界或者已经访问过了
        if (
          curX > m ||
          curY > m ||
          curX < 0 ||
          curY < 0 ||
          grid[curX][curY] == 1
        )
          continue;
        queue.push([curX, curY]);
        grid[curX][curY] = 1;
      }
    }
  }
  // 这里应该是-1  而不是depth 因为如果上面走通了上面就返回了  只有走不通没返回才到这里
  return -1;
};