1765. 地图中的最高点(bfs)

295 阅读2分钟

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

每日刷题第39天 2021.02.04

1765.地图中的最高点

题目

  • 给你一个大小为 m x n 的整数矩阵 isWater ,它代表了一个由 陆地 和 水域 单元格组成的地图。

  • 如果isWater[i][j] == 0 ,格子 (i, j) 是一个 陆地 格子。

  • 如果isWater[i][j] == 1 ,格子 (i, j) 是一个 水域 格子。

  • 你需要按照如下规则给每个单元格安排高度:

  • 每个格子的高度都必须是非负的。

  • 如果一个格子是是 水域 ,那么它的高度必须为 0 。

  • 任意相邻的格子高度差 至多 为 1 。当两个格子在正东、南、西、北方向上相互紧挨着,就称它们为相邻的格子。(也就是说它们有一条公共边)

  • 找到一种安排高度的方案,使得矩阵中的最高高度值最大 。

  • 请你返回一个大小为 m x n 的整数矩阵 height ,其中 height[i][j] 是格子 (i, j) 的高度。如果有多种解法,请返回任意一个 。

示例

  • 示例1 image.png
输入:isWater = [[0,1],[0,0]]
输出:[[1,0],[2,1]]
解释:上图展示了给各个格子安排的高度。
蓝色格子是水域格,绿色格子是陆地格。
  • 示例2 image.png
输入:isWater = [[0,0,1],[1,0,0],[0,0,0]]
输出:[[1,1,0],[0,1,1],[1,2,2]]
解释:所有安排方案中,最高可行高度为 2 。
任意安排方案中,只要最高高度为 2 且符合上述规则的,都为可行方案。

提示

  • m == isWater.length
  • n == isWater[i].length
  • 1 <= m, n <= 1000
  • isWater[i][j] 要么是 0 ,要么是 1 。
  • 至少有 1 个水域格子。

注意⚠️

  • 数组的shift()方法,从头移除元素,复杂度为o(n),放在循环中每次执行,可能会超时。
  • 改进方法:将每次需要从队列头部弹出的操作,单独开一个栈来进行,使用栈的pop\push方法,可以有效的降低时间复杂度。

解法

  • 回顾二叉树的bfs广度优先遍历,显示遍历第一层,再接着遍历第二层。
  • 放到本题,水域为0层,四周的为1层,依次往后类推。
  • 第一步:将所有的水域节点放入到队列中,step = 0
  • 第二步:声明visited数组,记录当前节点是否已经遍历过,遍历过的节点就不需要再加入到队列中。注:visited数组,不能使用map集合来存储,其实也没有必要。因为map集合的key为数组时,调用的时候,不能成功。
    • 举例:map(1) => ([0,1],1),使用map.get([0,1])无效,始终输出false
  • 第三步:声明height数组,记录陆地对应的高度。
  • 第三步:书写bfs的执行过程(可参考模版),其中弹出的数,会被删除。当弹出的数的四个方向的数都被放入队列中,同时需要标记visited数组值为1,因为已经进入过队列中。
var highestPeak = function(isWater) {
  // 使用bfs
  // 遍历出来,第一轮需要找的数
  let queue = [];
  let m = isWater.length;
  let n = isWater[0].length;
  let height = new Array(m);
  let visited = new Array(m);

  for(let i = 0; i < m; i++) {
    height[i] = new Array(n);
    visited[i] = new Array(n).fill(0);
  }

  for(let i = 0; i < m; i++) {
    for(let j = 0; j < n; j++) {
      if(isWater[i][j] == 1){
        queue.push([i,j]);
        height[i][j] = 0;
        visited[i][j] = 1;
      }
    }
  }

  // 将水域全部存储进去,为第0层,后续遍历第1层,依次往后
  let step = 0;
  let x,y;
  let next = [];
  while(queue.length) {
    let lenQ = queue.length;
    next = [];
    for(let i = 0; i < lenQ; i++) {
      // 超时会T,需要给每一层单独开一个栈来进行存储,需要使用pop和push,因为shift的复杂度是o(n)
      // let node = queue.shift();
      let node = queue.pop();
      // 上(x - 1,y)
      x = node[0];
      y = node[1];
      height[x][y] = step;

      if(x > 0 && !visited[x - 1][y]){
        next.push([x - 1, y]);
        visited[x - 1][y] = 1;
      }

      if(x < m - 1 && !visited[x + 1][y]) {
        // 下(x + 1,y)
        next.push([x + 1, y]);
        visited[x + 1][y] = 1;
      }
    
      if(y > 0 && !visited[x][y - 1]) {
        // 左(x,y - 1)
        next.push([x,y - 1]);
        visited[x][y - 1] = 1;
      }

      if(y < n - 1 && !visited[x][y + 1]) {
        // 右(x,y + 1)
        next.push([x,y + 1]);
        visited[x][y + 1] = 1;
      }
    }
    step++;
    queue = next;
  }
 return height;
};