Leetcode(934)——最短的桥
题目
在给定的二维二进制数组 A 中,存在两座岛。(岛是由四面相连的 1 1 1 形成的一个最大组。)
现在,我们可以将 0 0 0 变为 1 1 1,以使两座岛连接起来,变成一座岛。
返回必须翻转的 0 0 0 的最小数目。(可以保证答案至少是 1 1 1 。)
示例 1:
输入:A = [[0,1],[1,0]]
输出:1
示例 2:
输入:A = [[0,1,0],[0,0,0],[0,0,1]]
输出:2
示例 3:
输入:A = [[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
提示:
- 2 2 2 <= A.length == A[0].length <= 100 100 100
- A[i][j] == 0 0 0 或 A[i][j] == 1 1 1
题解
方法一:DFS 修改值 + BFS
思路
先找到其中一个岛屿中的一个点,然后进行
DFS
\texttt{DFS}
DFS 或
BFS
\texttt{BFS}
BFS,并把遍历到的所有值为 1 的点都变成
2
2
2,在遍历岛的过程中将 -1 岛的沿海一格全部加入队列。该队列就存储了 -1 岛的所有1格沿海。
然后使用该队列进行
BFS
\texttt{BFS}
BFS,同时把遍历到的点 0 变成 -1,并且记录遍历的步数。当填海后要加入下一个点时,如果碰到的是
1
1
1 ,则返回
m
i
n
min
min(最短桥的长度)。
因为我们从 -1 岛的海岸线开始进行广度优先搜索(一层为一条海岸线),当它们到达了 1 岛中的任意一个位置时,增加的海岸线长度层数,即搜索层次就是答案。
注意:先导入再填海会导致出现两个的岛格可能会导入相同的海格,比如: [ − 1 , 0 , − 1 ] [-1, 0, -1] [−1,0,−1] 这会导致大量重复海格进入队列。
代码实现
Leetcode 官方题解:
class Solution {
public:
vector<int> direction{-1, 0, 1, 0, -1};
// 主函数
int shortestBridge(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
queue<pair<int, int>> points;
// dfs寻找第一个岛屿,并把1全部赋值为2
bool flipped = false;
for (int i = 0; i < m; ++i) {
if (flipped) break;
for (int j = 0; j < n; ++j) {
if (grid[i][j] == 1) {
dfs(points, grid, m, n, i, j);
flipped = true;
break;
}
}
}
// bfs寻找第二个岛屿,并把过程中经过的0赋值为2
int x, y;
int level = 0;
while (!points.empty()){
++level;
int n_points = points.size();
while (n_points--) {
auto [r, c] = points.front();
points.pop();
for (int k = 0; k < 4; ++k) {
x = r + direction[k], y = c + direction[k+1];
if (x >= 0 && y >= 0 && x < m && y < n) {
if (grid[x][y] == 2) {
continue;
}
if (grid[x][y] == 1) {
return level;
}
points.push({x, y});
grid[x][y] = 2;
}
}
}
}
return 0;
}
// 辅函数
void dfs(queue<pair<int, int>>& points, vector<vector<int>>& grid, int m, int n, int i, int j) {
if (i < 0 || j < 0 || i == m || j == n || grid[i][j] == 2) {
return;
}
if (grid[i][j] == 0) {
points.push({i, j});
return;
}
grid[i][j] = 2;
dfs(points, grid, m, n, i - 1, j);
dfs(points, grid, m, n, i + 1, j);
dfs(points, grid, m, n, i, j - 1);
dfs(points, grid, m, n, i, j + 1);
}
};
我自己的:
class Solution {
vector<int> direction{-1, 0, 1, 0, -1};
int BFS(vector<vector<int>>& grid, queue<pair<int, int>>& q){
int row, col, nextr, nextc, min = 0, times;
while(!q.empty()){
min++; // 答案至少是 1
times = q.size();
while(times--){
row = q.front().first;
col = q.front().second;
q.pop();
grid[row][col] = -1; // 填海
for(int n = 0; n < 4; n++){
nextr = row + direction[n];
nextc = col + direction[n+1];
if(nextr < 0 || nextr >= grid.size() || nextc < 0 || nextc >= grid[0].size())
continue;
// 先导入再填海会导致出现两个的岛格可能会导入相同的海格,比如:[-1, 0, -1] 这会导致大量重复海格进入队列
if(grid[nextr][nextc] == 0)
q.push(make_pair(nextr, nextc));
else if(grid[nextr][nextc] == 1)
return min;
}
}
}
return 0;
}
void DFS(vector<vector<int>>& grid, queue<pair<int, int>>& q, int row, int col){
if(row < 0 || row >= grid.size() || col < 0 || col >= grid[0].size())
return;
if(grid[row][col] == 1){
grid[row][col] = -1;
DFS(grid, q, row+1, col);
DFS(grid, q, row-1, col);
DFS(grid, q, row, col+1);
DFS(grid, q, row, col-1);
}else if(grid[row][col] == 0) q.push(make_pair(row, col)); // 保存 -1 岛的一格沿海
}
public:
int shortestBridge(vector<vector<int>>& grid) {
// 有且只有两座岛,返回必须翻转的 0 的最小数目,可以保证答案至少是 1
// 分别标记两个岛,即一个岛为1,另二个岛为-1,海为0
// 只需要遍历一个岛然后将其修改为-1,而另一个不需要遍历修改,则可以区分它们
int row, col;
queue<pair<int, int>> q; // 保存 -1 岛的所有格子
for(row = 0; row < grid.size(); row++){
for(col = 0; col < grid[0].size(); col++){
if(grid[row][col] == 1){
DFS(grid, q, row, col);
break;
}
}
if(col < grid[0].size()) break;
}
// 从 -1 岛进行 BFS
return BFS(grid, q);
}
};
我自己的(改进——修改逻辑—— BFS 时先填海再查找导入):
class Solution {
vector<int> direction{-1, 0, 1, 0, -1};
int BFS(vector<vector<int>>& grid, queue<pair<int, int>>& q){
int row, col, nextr, nextc, min = 0, times;
while(!q.empty()){
min++; // 答案至少是 1
times = q.size();
while(times--){
row = q.front().first;
col = q.front().second;
q.pop();
for(int n = 0; n < 4; n++){
nextr = row + direction[n];
nextc = col + direction[n+1];
if(nextr < 0 || nextr >= grid.size() || nextc < 0 || nextc >= grid[0].size())
continue;
if(grid[nextr][nextc] == 0){
grid[nextr][nextc] = -1; // 填海
q.push(make_pair(nextr, nextc));
}else if(grid[nextr][nextc] == 1)
return min;
}
}
}
return 0;
}
void DFS(vector<vector<int>>& grid, queue<pair<int, int>>& q, int row, int col){
if(row < 0 || row >= grid.size() || col < 0 || col >= grid[0].size())
return;
if(grid[row][col] == 1){
grid[row][col] = -1;
DFS(grid, q, row+1, col);
DFS(grid, q, row-1, col);
DFS(grid, q, row, col+1);
DFS(grid, q, row, col-1);
}else if(grid[row][col] == 0){
grid[row][col] = -1; // 填海
q.push(make_pair(row, col)); // 保存 -1 岛的一格沿海
}
}
public:
int shortestBridge(vector<vector<int>>& grid) {
// 有且只有两座岛,返回必须翻转的 0 的最小数目,可以保证答案至少是 1
// 分别标记两个岛,即一个岛为1,另二个岛为-1,海为0
// 只需要遍历一个岛然后将其修改为-1,而另一个不需要遍历修改,则可以区分它们
int row, col;
queue<pair<int, int>> q; // 保存 -1 岛的所有格子
for(row = 0; row < grid.size(); row++){
for(col = 0; col < grid[0].size(); col++){
if(grid[row][col] == 1){
DFS(grid, q, row, col);
break;
}
}
if(col < grid[0].size()) break;
}
// 从 -1 岛进行 BFS
return BFS(grid, q);
}
};
复杂度分析
- 时间复杂度: O ( M N ) O(MN) O(MN),其中 M M M 和 N N N 分别是数组 g r i d grid grid 的行数和列数。
- 空间复杂度: O ( M N ) O(MN) O(MN)。