地图分析
难度:
标签:
题目描述
你现在手里有一份大小为 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过程中,如何保证不会重复处理已经访问过的单元格?
▷🦆
题解中提到,如果初始时队列为空或全是陆地则返回-1。这个处理逻辑是否能够正确处理所有边缘情况,例如极小的网格或极大的网格?
▷🦆
在执行BFS时,每个单元格最多入队和出队一次的逻辑是如何保证的?
▷