leetcode
leetcode 951 ~ 1000
地图分析

地图分析

难度:

标签:

题目描述

你现在手里有一份大小为 n x n 的 网格 grid,上面的每个 单元格 都用 0 和 1 标记好了。其中 0 代表海洋,1 代表陆地。

请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大的,并返回该距离。如果网格上只有陆地或者海洋,请返回 -1

我们这里说的距离是「曼哈顿距离」( Manhattan Distance):(x0, y0) 和 (x1, y1) 这两个单元格之间的距离是 |x0 - x1| + |y0 - y1| 。

 

示例 1:

输入:grid = [[1,0,1],[0,0,0],[1,0,1]]
输出:2
解释: 
海洋单元格 (1, 1) 和所有陆地单元格之间的距离都达到最大,最大距离为 2。

示例 2:

输入:grid = [[1,0,0],[0,0,0],[0,0,0]]
输出:4
解释: 
海洋单元格 (2, 2) 和所有陆地单元格之间的距离都达到最大,最大距离为 4。

 

提示:

  • n == grid.length
  • n == grid[i].length
  • 1 <= n <= 100
  • grid[i][j] 不是 0 就是 1

代码结果

运行时间: 109 ms, 内存: 16.8 MB


/*
 * 思路:
 * 使用Stream来遍历和处理数组。首先找出所有的陆地单元格并存储到队列中。
 * 然后使用类似的BFS方法处理,更新海洋单元格的距离。
 */
import java.util.*;
import java.util.stream.*;

public class Solution {
    public int maxDistance(int[][] grid) {
        int n = grid.length;
        Queue<int[]> queue = new LinkedList<>();
        // 将陆地单元格加入队列
        IntStream.range(0, n).forEach(i -> 
            IntStream.range(0, n).forEach(j -> {
                if (grid[i][j] == 1) {
                    queue.offer(new int[]{i, j});
                }
            })
        );
        // 如果全是海洋或者全是陆地,返回-1
        if (queue.isEmpty() || queue.size() == n * n) {
            return -1;
        }
        // 定义四个方向向量
        int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
        int maxDistance = -1;
        // 开始BFS
        while (!queue.isEmpty()) {
            int[] point = queue.poll();
            int x = point[0], y = point[1];
            for (int[] dir : directions) {
                int nx = x + dir[0], ny = y + dir[1];
                // 检查边界条件和是否已经访问过
                if (nx >= 0 && ny >= 0 && nx < n && ny < n && grid[nx][ny] == 0) {
                    grid[nx][ny] = grid[x][y] + 1;
                    maxDistance = Math.max(maxDistance, grid[nx][ny] - 1);
                    queue.offer(new int[]{nx, ny});
                }
            }
        }
        return maxDistance;
    }
}

解释

方法:

这个题解采用了广度优先搜索(BFS)的方法来解决问题。首先,它遍历整个网格,将所有陆地单元格的位置添加到队列中。接着,从所有陆地单元格开始同步扩展到相邻的海洋单元格,同时记录扩展的层数,这相当于从陆地到海洋单元格的最短距离。每扩展一层,距离就增加1。当不再有海洋单元格可以继续扩展时,最后记录的距离就是最大的海洋单元格到最近陆地单元格的距离。如果初始时队列为空(即没有陆地)或队列大小等于网格的单元格总数(即全是陆地),则直接返回-1。

时间复杂度:

O(n*n)

空间复杂度:

O(n*n)

代码细节讲解

🦆
为什么在广度优先搜索(BFS)的实现中,一旦海洋单元格被访问即被标记为2,而不是在它被加入队列时就标记?
在BFS中,标记海洋单元格为2的时机是在这个单元格确定要加入队列时,这样做是为了避免将同一个单元格多次加入队列。如果延迟标记直到单元格从队列中取出,那么在这个单元格被处理之前,相同的海洋单元格可能会被重复发现并多次加入队列,这会导致不必要的重复工作和增加内存消耗。因此,在单元格被加入队列的同时进行标记,是为了优化性能并防止重复处理。
🦆
在BFS过程中,如何保证不会重复处理已经访问过的单元格?
在此BFS实现中,通过将访问过的海洋单元格的值从0改为2来标记这些单元格已经被处理过。这种方式确保了一旦单元格被标记为2,它就不会再次被加入队列或被重新处理。每当扩展到一个新单元格时,算法检查其值是否为0(未访问的海洋单元格),只有满足这个条件的单元格才能被加入队列并在之后被处理。这样,每个单元格最多被处理一次,从而避免了重复处理。
🦆
题解中提到,如果初始时队列为空或全是陆地则返回-1。这个处理逻辑是否能够正确处理所有边缘情况,例如极小的网格或极大的网格?
是的,这个处理逻辑能够正确处理所有边缘情况。如果队列初始为空,意味着网格中没有陆地,因此没有起始点来进行BFS,所以返回-1是合理的。同样,如果队列的大小等于网格的单元格总数,意味着网格全是陆地,不存在海洋单元格,因此也应返回-1表示没有海洋单元格可以计算距离。这个逻辑适用于任何大小的网格,无论是极小的网格(如1x1)还是极大的网格。
🦆
在执行BFS时,每个单元格最多入队和出队一次的逻辑是如何保证的?
这是通过在单元格加入队列时立即将其标记为已访问(在本题中标记为2)来保证的。这种立即标记确保了一旦单元格被加入队列,就不会再次被加入。因此,每个单元格在整个BFS过程中只会被入队和出队一次。这不仅避免了重复处理,还提高了算法的效率和执行速度。

相关问题

离建筑物最近的距离

离建筑物最近的距离