题目描述
在给定的二维二进制数组 A
中,存在两座岛。(岛是由四面相连的 1
形成的一个最大组)现在,我们可以将 0
变为 1
,以使两座岛连接起来,变成一座岛。返回必须翻转的 0
的最小数目。
输入:[[0,1,0],[0,0,0],[0,0,1]]
输出:2
解题思路
算是比较好的一个题,直接想法是搜索:首先找到这两座岛,随后选择一座,将它不断向外延伸一圈,直到到达了另一座岛。在寻找这两座岛时,我们使用深度优先搜索。在向外延伸时,我们使用广度优先搜索。(DFS + BFS
)
-
DFS + BFS(推荐):
dfs
找到第一个岛修改原数组为2;将第二个岛入队列,进行宽搜,查找最小距离; -
两次BFS:第一个
bfs
,将其中一个岛渲染成2
,并记录边界,只要某点四个连接点有一个为0
,就是边界,添加进队列,用于第二个bfs的搜索。第二个bfs
同时从各个边界出发,记录路径即可。(其实没有必要找边界,直接把第一座桥整体添加进队列就可以,如法一)
参考代码
class Solution {
public:
int dirR[4] = {-1, 0, 1, 0};
int dirC[4] = {0, 1, 0, -1};
queue<pair<int, int> > q; // 也可以不用pair,定义一个结构体point
int shortestBridge(vector<vector<int>> &A) {
// dfs 查找两个岛,标记第一个岛为2(这里完全可以用i,j两个循环的方式写)
for (int i = 0; i < A.size() * A[0].size(); i++) {
if (A[i / A[0].size()][i % A[0].size()] == 1) {
dfs(A, i / A[0].size(), i % A[0].size());
break;
}
}
int step = 0;
// 第一个岛里面的所有点,向外扩展,记录层数,当遇到第二个岛的时候,返回层数 bfs
while (!q.empty()) {
int nums = q.size(); // 当前层的元素数
while (nums--){
auto tmp_pair = q.front(); q.pop();
// 往四个方向 bfs
for (int i = 0; i < 4; i++){
int r = tmp_pair.first + dirR[i];
int c = tmp_pair.second + dirC[i];
if (r < 0 || r >= A.size() || c < 0 || c >= A[0].size() || A[r][c] == 2) continue;
if (A[r][c] == 1) return step; // 到达另一座桥
A[r][c] = 2; // 着色
q.push({r, c});
}
}
step++; // 相当于往外扩展了一圈之后,step才加加
}
return step;
}
// dfs(目的是把其中一座桥全部装进queue中,并着色为 2)
void dfs(vector<vector<int>> &A, int i, int j) {
q.push({i, j}); // 把其中一个桥全部装进queue
A[i][j] = 2; // 着色
for (int d = 0; d < 4; d++) {
int r = i + dirR[d];
int c = j + dirC[d];
if(r >= 0 && r < A.size() && c >= 0 && c < A[0].size() && A[r][c] == 1)
dfs(A, r, c);
}
}
};