1、题目描述:
给你一个大小为 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
中恰有两个岛
2、代码:
/*
* @file shortest_bridge.cpp
* @brief 在二进制矩阵中找到连接两个岛屿的最短路径
*
* 给定一个 n x n 的二进制矩阵 grid,其中 1 表示陆地,0 表示水域。
* 岛屿是由四面相连的 1 组成的组。假设矩阵中有且仅有两个岛屿,
* 需要找到连接两个岛屿的最短桥(即翻转最少数量的0使两个岛屿相连)。
*
* 实现思路:
* 1. 使用BFS标记第一个岛屿的所有单元格(标记为2)
* 2. 从第一个岛屿的所有边界开始进行多源BFS扩展
* 3. 当遇到第二个岛屿(值为1的单元格)时,返回扩展的步数
*
*/
#include <vector>
#include <queue>
using namespace std;
class Solution {
public:
int shortestBridge(vector<vector<int>>& grid) {
int n = grid.size();
// 定义四个移动方向:上、下、左、右
vector<vector<int>> dirs = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
// 主队列:用于后续的多源BFS扩展
queue<pair<int, int>> q;
// 步骤1: 找到并标记第一个岛屿
bool found = false; // 标记是否已找到第一个岛屿
for (int i = 0; i < n && !found; ++i) {
for (int j = 0; j < n && !found; ++j) {
if (grid[i][j] == 1) { // 发现第一个岛屿的起始点
// 使用BFS标记整个岛屿
queue<pair<int, int>> islandQ; // 岛屿内部BFS专用队列
islandQ.push({ i, j });
grid[i][j] = 2; // 将起始点标记为2(表示属于第一个岛屿)
q.push({ i, j }); // 同时加入主队列(作为多源BFS的起点)
// BFS遍历并标记第一个岛屿的所有单元格
while (!islandQ.empty()) {
auto [x, y] = islandQ.front();
islandQ.pop();
// 检查四个方向
for (auto& dir : dirs) {
int nx = x + dir[0], ny = y + dir[1];
// 检查新位置是否在网格内且属于第一个岛屿(值为1)
if (nx >= 0 && ny >= 0 && nx < n && ny < n && grid[nx][ny] == 1) {
grid[nx][ny] = 2; // 标记为属于第一个岛屿
islandQ.push({ nx, ny }); // 加入岛屿BFS队列
q.push({ nx, ny }); // 加入主队列(作为扩展起点)
}
}
}
found = true; // 标记第一个岛屿已处理完成
}
}
}
// 步骤2: 从第一个岛屿边界开始进行多源BFS扩展
int step = 0; // 记录扩展的步数(即桥的长度)
while (!q.empty()) {
int size = q.size(); // 当前层的节点数量
// 遍历当前层的所有节点
for (int i = 0; i < size; ++i) {
auto [x, y] = q.front();
q.pop();
// 向四个方向扩展
for (auto& dir : dirs) {
int nx = x + dir[0], ny = y + dir[1];
// 检查新位置是否在网格内
if (nx >= 0 && ny >= 0 && nx < n && ny < n) {
if (grid[nx][ny] == 1) {
// 找到第二个岛屿,返回当前步数(即桥的最小长度)
return step;
}
else if (grid[nx][ny] == 0) {
// 遇到水域,标记为已访问(避免重复处理)
grid[nx][ny] = 2;
// 加入队列继续扩展
q.push({ nx, ny });
}
}
}
}
step++; // 完成一层扩展,步数增加
}
return -1; // 理论上不会执行(因为保证有两个岛屿)
}
};
3、解题思路:
-
识别第一个岛屿 :
- 遍历矩阵,找到第一个值为
1
的点,使用 BFS 或 DFS 遍历其所有相连的1
,标记为第一个岛屿。 - 在这个过程中,可以使用一个
dist
数组来记录每个点的距离,初始岛屿的点距离为0
。
- 遍历矩阵,找到第一个值为
-
BFS 扩展第一个岛屿 :
- 从第一个岛屿的所有点出发,进行 BFS 扩展,逐步向外探索。
- 每次扩展一个
0
,将其标记为已访问,并记录当前扩展的步数。 - 当扩展过程中遇到另一个岛屿的
1
时,此时的步数即为需要转换的最小水域数量。
-
返回结果 :
- 一旦找到连接两个岛屿的最短路径,立即返回该路径长度。