这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战
题目
你现在手里有一份大小为 n x n
的 网格 grid
,上面的每个 单元格 都用 0
和 1
标记好了。其中 0
代表海洋,1
代表陆地,请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大的。如果网格上只有陆地或者海洋,请返回 -1
。
我们这里说的距离是「曼哈顿距离」( Manhattan Distance):(x0, y0)
和 (x1, y1)
这两个单元格之间的距离是 |x0 - x1| + |y0 - y1|
。
示例
输入:grid = [[1,0,1],[0,0,0],[1,0,1]]
输出:2
解释:
海洋单元格 (1, 1) 和所有陆地单元格之间的距离都达到最大,最大距离为 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
解题思路
BFS
该题需要找到海洋跟最近一块陆地的最大距离,算是一道比较典型的广度优先搜索的题。我们可以将岛屿上所有的陆地都入队,通过该陆地为起点,将其周边的海洋都标记上距离,一圈圈的扩散开,直至最终的交汇处即可求得最远的距离。
- 找到所有陆地坐标点,将其入队
- 遍历队列中的坐标点,获取其周边的海洋左边,更新距离
- 将更新距离后的坐标点入队,重复 步骤2 直至队列为空
- 判断当前岛屿是否全为陆地或海洋
- 返回最后遍历到的坐标点,该点即为最长距离
class Solution {
public int maxDistance(int[][] grid) {
Queue<Integer> queue = new LinkedList<>();
int m = grid.length, n = grid[0].length;
// 查找陆地,将其入队
for(int x = 0; x < m; ++x){
for(int y = 0; y < n; ++y){
if(grid[x][y] == 1){
// 根据提示中的边界,优化两个坐标点为一个数值进行保存
queue.add(x * 1000 + y);
}
}
}
int idx = -1;
// 陆地判断
boolean flag = true;
// 四周坐标
int[][] arr = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
// 遍历队列中的坐标点
while(!queue.isEmpty()){
// 反序列化,得到坐标点
idx = queue.poll();
int x = idx / 1000;
int y = idx % 1000;
// 遍历四周
for(int[] a : arr){
int x0 = x + a[0];
int y1 = y + a[1];
// 非海洋则跳过
if(x0 >= m || x0 < 0 || y1 >= n || y1 < 0 || grid[x0][y1] != 0){
continue;
}
// 更新并入队
flag = false;
grid[x0][y1] = grid[x][y] + 1;
queue.add(x0 * 1000 + y1);
}
}
// 海洋/陆地校验
if(idx == -1 || flag){
return -1;
}
// 得到最终的坐标点返回结果
return grid[idx / 1000][idx % 1000] - 1;
}
}
DP
我们也可以使用动态规划来解题,先从左上到右下,将海洋的坐标更新为距离左边陆地的长度,再从右下到左上遍历多一遍,修复一下海洋与右边陆地的长度,这样即可得到海洋在两块陆地中的一个中间值。
- 从左上到右下,计算出海洋与左上陆地之间的距离
- 从右下到左上,计算出海洋与右下陆地至今的距离
- 更新最长距离变量
class Solution {
public int maxDistance(int[][] grid) {
boolean isLand = false;
int m = grid.length, n = grid[0].length;
// 从左上到右下,计算出海洋与左上陆地之间的距离
for(int x = 0; x < m; ++x){
for(int y = 0; y < n; ++y){
// 当前坐标点为陆地,不需要计算,直接跳过
if(grid[x][y] == 1){
isLand = true;
continue;
}
// 初始化
if(grid[x][y] == 0){
grid[x][y] = m * n;
}
// 不是边界,则取前一个值做比较,得到当前海洋距离最近一块陆地的最长距离
if(x > 0){
grid[x][y] = Math.min(grid[x][y], grid[x - 1][y] + 1);
}
if(y > 0){
grid[x][y] = Math.min(grid[x][y], grid[x][y - 1] + 1);
}
}
}
// 从右下到左上,计算出海洋与右下陆地至今的距离
int result = 0;
for(int x = m - 1; x >= 0; --x){
for(int y = n - 1; y >= 0; --y){
// 当前坐标点为陆地,不需要计算,直接跳过
if(grid[x][y] == 1){
continue;
}
// 不是边界,则取前一个值做比较,得到当前海洋距离最近一块陆地的最长距离
if(x < m - 1){
grid[x][y] = Math.min(grid[x][y], grid[x + 1][y] + 1);
}
if(y < m - 1){
grid[x][y] = Math.min(grid[x][y], grid[x][y + 1] + 1);
}
// 更新最长距离
result = Math.max(result, grid[x][y]);
}
}
// 全海洋返回 -1, 陆地返回最长距离
return isLand ? result - 1 : -1;
}
}