
思路:并查集+BFS
- 核心思想:用并查集构建出来两个岛,然后依次遍历岛上的点向上下左右走,直到走到对方岛屿返回答案。
- 首先使用并查集构建小岛,也就是将上下左右相连的小岛连通;
- 此处不需要判断连通的
isConnected();
- 用
find()找爸,union()连通两个点即可。
- 然后使用队列和哈希表分别存放两个岛屿和不同节点对应步数;
- 将不同岛屿的节点依次填入相应队列ild,作为待遍历节点;
- 同时将节点加入哈希表stp(index,steps),并将步数初始化为0;
- 最后开始架桥;
- 从较小的岛屿开始走【循环次数较少】;
- 进入
update(出发岛, 出发岛节点对应步数, 对方岛)计算步数,针对当前出发岛节点个数进行循环计算:
- 取队头节点cur向前(上下左右)走一步到nxt:
- 若越界或还是在自己岛上,则向下一个方向走;
- 若到达对方岛,则返回所走步数总和,即cur.stp+nxt.stp+1;
- 若到河里,则将该节点作为待遍历节点入队到出发岛,并记录对应步数;
- 延续该步骤结束后,出发岛ild内为周围最近一圈的河节点。
- 根据返回值判断:
- 若到达对方岛,则直接返回结果;
- 否则重复上述步骤,根据新入队的中间节点【河】进行进一步判断,整个过程可看作两个岛不断向外辐射直至碰到对方领域。
Java
class Solution {
static int N = 10010;
static int[] p = new int[N];
static int[][] dir = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int n;
int getIdx(int x, int y) {
return x * n + y;
}
int find(int x) {
if (p[x] != x)
p[x] = find(p[x]);
return p[x];
}
void union(int a, int b) {
p[find(a)] = p[find(b)];
}
public int shortestBridge(int[][] grid) {
n = grid.length;
for (int i = 0; i <= n * n; i++)
p[i] = i;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0)
continue;
for (int[] d : dir) {
int x = i + d[0], y = j + d[1];
if (x < 0 || x >= n || y < 0 || y >= n)
continue;
if (grid[x][y] == 0)
continue;
union(getIdx(i, j), getIdx(x, y));
}
}
}
int a = -1, b = -1;
Deque<int[]> ild1 = new ArrayDeque<>(), ild2 = new ArrayDeque<>();
Map<Integer, Integer> stp1 = new HashMap<>(), stp2 = new HashMap<>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0)
continue;
int idx = getIdx(i, j), root = find(idx);
if (a == -1)
a = root;
else if (a != root && b == -1)
b = root;
if (root == a) {
ild1.addLast(new int[]{i, j});
stp1.put(idx, 0);
}
else if (root == b) {
ild2.addLast(new int[]{i, j});
stp2.put(idx, 0);
}
}
}
while (!ild1.isEmpty() && !ild2.isEmpty()) {
int res = -1;
if(ild1.size() < ild2.size())
res = update(ild1, stp1, stp2);
else
res = update(ild2, stp2, stp1);
if (res != -1)
return res - 1;
}
return -1;
}
int update(Deque<int[]> ild, Map<Integer, Integer> stp1, Map<Integer, Integer> stp2) {
int sz = ild.size();
while (sz-- > 0) {
int[] cur = ild.pollFirst();
int x = cur[0], y = cur[1], idx = getIdx(x, y), root = find(idx);
for (int[] d : dir) {
int nx = x + d[0], ny = y + d[1], nidx = getIdx(nx, ny);
if (nx < 0 || nx >= n || ny < 0 || ny >= n)
continue;
if (stp1.containsKey(nidx))
continue;
if (stp2.containsKey(nidx))
return stp1.get(idx) + 1 + stp2.get(nidx);
ild.addLast(new int[]{nx, ny});
stp1.put(nidx, stp1.get(idx) + 1);
}
}
return -1;
}
}
- 时间复杂度:O(n2)
- 空间复杂度:O(n2)
C++
class Solution {
public:
const static int N = 10010;
int p[N];
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int n;
int getIdx(int x, int y) {
return x * n + y;
}
int find(int x) {
if (p[x] != x)
p[x] = find(p[x]);
return p[x];
}
void unionn(int a, int b) {
p[find(a)] = p[find(b)];
}
int shortestBridge(vector<vector<int>>& grid) {
n = grid.size();
for (int i = 0; i <= n * n; i++)
p[i] = i;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0)
continue;
for (auto d : dir) {
int x = i + d[0], y = j + d[1];
if (x < 0 || x >= n || y < 0 || y >= n)
continue;
if (grid[x][y] == 0)
continue;
unionn(getIdx(i, j), getIdx(x, y));
}
}
}
int a = -1, b = -1;
queue<pair<int, int>> ild1, ild2;
unordered_map<int, int> stp1, stp2;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0)
continue;
int idx = getIdx(i, j), root = find(idx);
if (a == -1)
a = root;
else if (a != root && b == -1)
b = root;
if (root == a) {
ild1.emplace(i, j);
stp1[idx] = 0;
}
else if (root == b) {
ild2.emplace(i, j);
stp2[idx] = 0;
}
}
}
while (!ild1.empty() && !ild2.empty()) {
int res = -1;
if(ild1.size() < ild2.size())
res = update(ild1, stp1, stp2);
else
res = update(ild2, stp2, stp1);
if (res != -1)
return res - 1;
}
return -1;
}
int update(queue<pair<int, int>>& ild, unordered_map<int, int>& stp1, unordered_map<int, int>& stp2) {
int sz = ild.size();
while (sz-- > 0) {
auto [x, y] = ild.front();
ild.pop();
int idx = getIdx(x, y), root = find(idx);
for (auto d : dir) {
int nx = x + d[0], ny = y + d[1], nidx = getIdx(nx, ny);
if (nx < 0 || nx >= n || ny < 0 || ny >= n)
continue;
if (stp1.count(nidx))
continue;
if (stp2.count(nidx))
return stp1[idx] + 1 + stp2[nidx];
ild.emplace(nx, ny);
stp1[nidx] = stp1[idx] + 1;
}
}
return -1;
}
};
- 时间复杂度:O(n2)
- 空间复杂度:O(n2)
总结
- 前几天刚学过并查集所以想到了用这个构建小岛,但是架桥还是有点复杂……
- 其他还有很多方法,比如:
- DFS+BFS的方法,即用DFS先确定一座岛,然后用BFS向外扩散到另一座岛;
- 该方法复杂度根据所决定的出发岛存在一定随机性,且需注意标记已遍历位置;
- 适应性BFS,在插入节点时不是一味向结尾添加,若遇到本岛节点直接向头添加;
- 一贯的算法题没有心力再搞Rust……