题目链接:https://leetcode.cn/problems/shortest-bridge/
题目描述:
给你一个大小为 n x n
的二元矩阵 grid
,其中 1
表示陆地,0
表示水域。
岛 是由四面相连的 1
形成的一个最大组,即不会与非组内的任何其他 1
相连。grid
中 恰好存在两座岛 。
你可以将任意数量的 0
变为 1
,以使两座岛连接起来,变成 一座岛 。
返回必须翻转的 0
的最小数目。
示例 1:
输入:grid = [[0,1],[1,0]]
输出:1
示例 2:
输入:grid = [[0,1,0],[0,0,0],[0,0,1]]
输出:2
示例 3:
输入:grid = [[1,1,1,1,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,0,1],[1,1,1,1,1]]
输出:1
提示:
n == grid.length == grid[i].length
2 <= n <= 100
grid[i][j]
为0
或1
grid
中恰有两个岛
解法:深度优先遍历+广度优先遍历
创建一个队列deque用于存放岛屿边缘坐标。
先遍历二元矩阵,找到任意一块陆地,分别向上下左右四个方向开始深度优先遍历,当遇到陆地时,代表是同一块岛屿,将其标记为2,当遇到水域时,表示到了岛屿边缘,将其也标记为2,并添加到一个队列中。
这样遍历一次,就得到了第一块岛屿的所有边缘坐标。
然后在通过对这边边缘坐标广度优先遍历,让其向外扩张,每次扩张一步,当遇到水域,就将其标记为2,添加到deque中,直到遇到陆地,表示两岛相连,返回扩张次数。
代码:
class Solution {
Deque<int[]> deque = new ArrayDeque<>();
int[][] dp = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int[][] grid;
boolean[][] isFlag;
int result = 0;
public int shortestBridge(int[][] grid) {
this.grid = grid;
boolean flag = false;
this.isFlag = new boolean[grid.length][grid[0].length];
for (int i = 0; i < grid.length && !flag; i++) {
for (int j = 0; j < grid[0].length && !flag; j++) {
if (grid[i][j] == 1) {
flag = true;
calc(i, j);
}
}
}
while (!deque.isEmpty()) {
result++;
int len = deque.size();
for (int i = 0; i < len; i++) {
int[] temp = deque.pollFirst();
for (int[] ints : dp) {
int nex = temp[0] + ints[0], ney = temp[1] + ints[1];
if (isLegal(nex, ney) && grid[nex][ney] == 0) {
deque.addLast(new int[]{nex, ney});
grid[nex][ney] = 2;
} else if (isLegal(nex, ney) && grid[nex][ney] == 1) {
return result;
}
}
}
}
return result;
}
public void calc(int row, int col) {
if (!isLegal(row, col) || grid[row][col] == 2) return;
if (grid[row][col] == 0) {
grid[row][col] = 2; // 将边界向外扩展1层岛屿(val=2)
deque.addLast(new int[]{row, col});
return;
}
grid[row][col] = 2; // 为岛屿打标记(val=2)
for (int[] c : dp) calc(row + c[0], col + c[1]); // 深度遍历某个格子的四个方向
}
public boolean isLegal(int row, int column) {
return row >= 0 && row < grid.length && column >= 0 && column < grid[0].length;
}
}