Skip to content

Commit dcb4db7

Browse files
Merge pull request SharingSource#722 from SharingSource/ac_oier
✨feat: add 934
2 parents 8571c8e + 2c08704 commit dcb4db7

File tree

3 files changed

+297
-0
lines changed

3 files changed

+297
-0
lines changed

Index/图论 双向 BFS.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
| [433. 最小基因变化](https://leetcode-cn.com/problems/minimum-genetic-mutation/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/minimum-genetic-mutation/solution/by-ac_oier-74b4/) | 中等 | 🤩🤩🤩🤩🤩 |
55
| [752. 打开转盘锁](https://leetcode-cn.com/problems/open-the-lock/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/open-the-lock/solution/gong-shui-san-xie-yi-ti-shuang-jie-shuan-wyr9/) | 中等 | 🤩🤩🤩🤩 |
66
| [815. 公交路线](https://leetcode-cn.com/problems/bus-routes/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/bus-routes/solution/gong-shui-san-xie-yi-ti-shuang-jie-po-su-1roh/) | 困难 | 🤩🤩🤩🤩 |
7+
| [934. 最短的桥](https://leetcode.cn/problems/shortest-bridge/) | [LeetCode 题解链接](https://leetcode.cn/problems/shortest-bridge/solution/by-ac_oier-56ly/) | 中等 | 🤩🤩🤩🤩 |
78
| [1345. 跳跃游戏 IV](https://leetcode-cn.com/problems/jump-game-iv/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/jump-game-iv/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-q9tb1/) | 困难 | 🤩🤩🤩🤩🤩 |
89
| [2059. 转化数字的最小运算数](https://leetcode-cn.com/problems/minimum-operations-to-convert-number/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/minimum-operations-to-convert-number/solution/gong-shui-san-xie-shuang-xiang-bfs-mo-ba-uckg/) | 中等 | 🤩🤩🤩🤩🤩 |
910

Index/并查集.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
| [815. 公交路线](https://leetcode-cn.com/problems/bus-routes/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/bus-routes/solution/gong-shui-san-xie-yi-ti-shuang-jie-po-su-1roh/) | 困难 | 🤩🤩🤩🤩 |
88
| [827. 最大人工岛](https://leetcode.cn/problems/making-a-large-island/) | [LeetCode 题解链接](https://leetcode.cn/problems/making-a-large-island/solution/by-ac_oier-1kmp/) | 困难 | 🤩🤩🤩🤩🤩 |
99
| [886. 可能的二分法](https://leetcode.cn/problems/possible-bipartition/) | [LeetCode 题解链接](https://leetcode.cn/problems/possible-bipartition/solution/by-ac_oier-6j0n/) | 中等 | 🤩🤩🤩🤩 |
10+
| [934. 最短的桥](https://leetcode.cn/problems/shortest-bridge/) | [LeetCode 题解链接](https://leetcode.cn/problems/shortest-bridge/solution/by-ac_oier-56ly/) | 中等 | 🤩🤩🤩🤩 |
1011
| [952. 按公因数计算最大组件大小](https://leetcode.cn/problems/largest-component-size-by-common-factor/) | [LeetCode 题解链接](https://leetcode.cn/problems/largest-component-size-by-common-factor/solution/by-ac_oier-mw04/) | 困难 | 🤩🤩🤩🤩 |
1112
| [1020. 飞地的数量](https://leetcode-cn.com/problems/number-of-enclaves/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/number-of-enclaves/solution/gong-shui-san-xie-bing-cha-ji-dfs-yun-yo-oyh1/) | 中等 | 🤩🤩🤩 |
1213
| [1631. 最小体力消耗路径](https://leetcode-cn.com/problems/path-with-minimum-effort/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/path-with-minimum-effort/solution/fan-zheng-fa-zheng-ming-si-lu-de-he-fa-x-ohby/) | 中等 | 🤩🤩🤩 |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[934. 最短的桥](https://leetcode.cn/problems/shortest-bridge/solution/by-ac_oier-56ly/)** ,难度为 **中等**
4+
5+
Tag : 「并查集」、「双向 BFS」
6+
7+
8+
9+
给你一个大小为 `n x n` 的二元矩阵 `grid`,其中 `1` 表示陆地,`0` 表示水域。
10+
11+
岛 是由四面相连的 `1` 形成的一个最大组,即不会与非组内的任何其他 `1` 相连。`grid` 中 恰好存在两座岛 。
12+
13+
你可以将任意数量的 `0` 变为 `1` ,以使两座岛连接起来,变成 一座岛 。
14+
15+
返回必须翻转的 `0` 的最小数目。
16+
17+
示例 1:
18+
```
19+
输入:grid = [[0,1],[1,0]]
20+
21+
输出:1
22+
```
23+
示例 2:
24+
```
25+
输入:grid = [[0,1,0],[0,0,0],[0,0,1]]
26+
27+
输出:2
28+
```
29+
示例 3:
30+
```
31+
输入: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]]
32+
33+
输出:1
34+
```
35+
36+
提示:
37+
* $n = grid.length = grid[i].length$
38+
* $2 <= n <= 100$
39+
* `grid[i][j]``0``1`
40+
* `grid` 中恰有两个岛
41+
42+
---
43+
44+
### 并查集 + 双向 BFS
45+
46+
**使用「并查集」将两个岛标记出来,然后将两个岛的点分别入队,再运用「双向 BFS」来找最短通路。**
47+
48+
为了方便,我们将 `grid` 记为 `g`
49+
50+
对于所有满足 $g[i][j] = 1$ 的节点与其四联通的方向,值同为 $1$ 的节点进行并查集连通性维护。
51+
52+
随后建立两个队列 `d1``d2` 分别存储两个岛的节点(以二元组 $(x, y)$ 的方式出入队),并使用两个哈希表 `m1``m2` 来记录从两岛屿出发到达该节点所消耗的步数(以节点的一维编号为 `key`,以消耗步数为 `value`)。
53+
54+
最后是使用「双向 BFS」来求解使两岛屿联通的最小通路:每次从队列中较少的进行拓展,只有尚未被处理过的节点(没有被当前哈希表所记录)才进行入队并更新消耗步数,当拓展节点在另外一个队列对应的哈希表表中出现过,说明找到了最短通路。
55+
56+
Java 代码:
57+
```Java
58+
class Solution {
59+
static int N = 10010;
60+
static int[] p = new int[N];
61+
static int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
62+
int n;
63+
int getIdx(int x, int y) {
64+
return x * n + y;
65+
}
66+
int find(int x) {
67+
if (p[x] != x) p[x] = find(p[x]);
68+
return p[x];
69+
}
70+
void union(int x, int y) {
71+
p[find(x)] = p[find(y)];
72+
}
73+
public int shortestBridge(int[][] g) {
74+
n = g.length;
75+
for (int i = 0; i <= n * n; i++) p[i] = i;
76+
for (int i = 0; i < n; i++) {
77+
for (int j = 0; j < n; j++) {
78+
if (g[i][j] == 0) continue;
79+
for (int[] di : dirs) {
80+
int x = i + di[0], y = j + di[1];
81+
if (x < 0 || x >= n || y < 0 || y >= n) continue;
82+
if (g[x][y] == 0) continue;
83+
union(getIdx(i, j), getIdx(x, y));
84+
}
85+
}
86+
}
87+
int a = -1, b = -1;
88+
Deque<int[]> d1 = new ArrayDeque<>(), d2 = new ArrayDeque<>();
89+
Map<Integer, Integer> m1 = new HashMap<>(), m2 = new HashMap<>();
90+
for (int i = 0; i < n; i++) {
91+
for (int j = 0; j < n; j++) {
92+
if (g[i][j] == 0) continue;
93+
int idx = getIdx(i, j), root = find(idx);
94+
if (a == -1) a = root;
95+
else if (a != root && b == -1) b = root;
96+
if (root == a) {
97+
d1.addLast(new int[]{i, j});
98+
m1.put(idx, 0);
99+
} else if (root == b) {
100+
d2.addLast(new int[]{i, j});
101+
m2.put(idx, 0);
102+
}
103+
}
104+
}
105+
while (!d1.isEmpty() && !d2.isEmpty()) {
106+
int t = -1;
107+
if (d1.size() < d2.size()) t = update(d1, m1, m2);
108+
else t = update(d2, m2, m1);
109+
if (t != -1) return t - 1;
110+
}
111+
return -1; // never
112+
}
113+
int update(Deque<int[]> d, Map<Integer, Integer> m1, Map<Integer, Integer> m2) {
114+
int sz = d.size();
115+
while (sz-- > 0) {
116+
int[] info = d.pollFirst();
117+
int x = info[0], y = info[1], idx = getIdx(x, y), step = m1.get(idx);
118+
for (int[] di : dirs) {
119+
int nx = x + di[0], ny = y + di[1], nidx = getIdx(nx, ny);
120+
if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue;
121+
if (m1.containsKey(nidx)) continue;
122+
if (m2.containsKey(nidx)) return step + 1 + m2.get(nidx);
123+
d.addLast(new int[]{nx, ny});
124+
m1.put(nidx, step + 1);
125+
}
126+
}
127+
return -1;
128+
}
129+
}
130+
```
131+
TypeScript 代码:
132+
```TypeScript
133+
let n: number
134+
const p = new Array<number>(10010).fill(0)
135+
const dirs = [[0,1],[0,-1],[1,0],[-1,0]]
136+
function shortestBridge(g: number[][]): number {
137+
function getIdx(x: number, y: number): number {
138+
return x * n + y
139+
}
140+
function find(x: number): number {
141+
if (p[x] != x) p[x] = find(p[x])
142+
return p[x]
143+
}
144+
function union(x: number, y: number): void {
145+
p[find(x)] = p[find(y)]
146+
}
147+
function update(d: Array<Array<number>>, m1: Map<number, number>, m2: Map<number, number>): number {
148+
let sz = d.length
149+
while (sz-- > 0) {
150+
const info = d.shift()
151+
const x = info[0], y = info[1], idx = getIdx(x, y), step = m1.get(idx)
152+
for (const di of dirs) {
153+
const nx = x + di[0], ny = y + di[1], nidx = getIdx(nx, ny)
154+
if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue
155+
if (m1.has(nidx)) continue
156+
if (m2.has(nidx)) return step + 1 + m2.get(nidx)
157+
d.push([nx, ny])
158+
m1.set(nidx, step + 1)
159+
}
160+
}
161+
return -1
162+
}
163+
n = g.length
164+
for (let i = 0; i < n * n; i++) p[i] = i
165+
for (let i = 0; i < n; i++) {
166+
for (let j = 0; j < n; j++) {
167+
if (g[i][j] == 0) continue
168+
for (const di of dirs) {
169+
const x = i + di[0], y = j + di[1]
170+
if (x < 0 || x >= n || y < 0 || y >= n) continue
171+
if (g[x][y] == 0) continue
172+
union(getIdx(i, j), getIdx(x, y))
173+
}
174+
}
175+
}
176+
let a = -1, b = -1
177+
const d1 = new Array<number[]>(), d2 = new Array<number[]>()
178+
const m1 = new Map<number, number>(), m2 = new Map<number, number>()
179+
for (let i = 0; i < n; i++) {
180+
for (let j = 0; j < n; j++) {
181+
if (g[i][j] == 0) continue
182+
const idx = getIdx(i, j), root = find(idx)
183+
if (a == -1) a = root
184+
else if (a != root && b == -1) b = root
185+
if (a == root) {
186+
d1.push([i, j])
187+
m1.set(idx, 0)
188+
} else if (b == root) {
189+
d2.push([i, j])
190+
m2.set(idx, 0)
191+
}
192+
}
193+
}
194+
while (d1.length != 0 && d2.length != 0) {
195+
let t = -1
196+
if (d1.length < d2.length) t = update(d1, m1, m2)
197+
else t = update(d2, m2, m1)
198+
if (t != -1) return t - 1
199+
}
200+
return -1
201+
}
202+
```
203+
Python 代码:
204+
```Python
205+
import queue
206+
207+
class Solution:
208+
def shortestBridge(self, g: List[List[int]]) -> int:
209+
def getIdx(x, y):
210+
return x * n + y
211+
212+
def find(x):
213+
if p[x] != x:
214+
p[x] = find(p[x])
215+
return p[x]
216+
217+
def union(x, y):
218+
p[find(x)] = p[find(y)]
219+
220+
def update(d, cur, other):
221+
sz = d.qsize()
222+
while sz != 0:
223+
x, y = d.get()
224+
idx, step = getIdx(x, y), cur.get(getIdx(x, y))
225+
for di in dirs:
226+
nx, ny = x + di[0], y + di[1]
227+
nidx = getIdx(nx, ny)
228+
if nx < 0 or nx >= n or ny < 0 or ny >= n:
229+
continue
230+
if nidx in cur:
231+
continue
232+
if nidx in other:
233+
return step + 1 + other.get(nidx)
234+
d.put([nx, ny])
235+
cur[nidx] = step + 1
236+
sz -= 1
237+
return -1
238+
239+
n = len(g)
240+
p = [i for i in range(n * n + 10)]
241+
dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]]
242+
for i in range(n):
243+
for j in range(n):
244+
if g[i][j] == 0:
245+
continue
246+
for di in dirs:
247+
x, y = i + di[0], j + di[1]
248+
if x < 0 or x >= n or y < 0 or y >= n:
249+
continue
250+
if g[x][y] == 0:
251+
continue
252+
union(getIdx(i, j), getIdx(x, y))
253+
a, b = -1, -1
254+
d1, d2 = queue.Queue(), queue.Queue()
255+
m1, m2 = {}, {}
256+
for i in range(n):
257+
for j in range(n):
258+
if g[i][j] == 0:
259+
continue
260+
idx, root = getIdx(i, j), find(getIdx(i, j))
261+
if a == -1:
262+
a = root
263+
elif a != root and b == -1:
264+
b = root
265+
if a == root:
266+
d1.put([i, j])
267+
m1[idx] = 0
268+
elif b == root:
269+
d2.put([i, j])
270+
m2[idx] = 0
271+
while not d1.empty() and not d2.empty():
272+
t = -1
273+
if d1.qsize() < d2.qsize():
274+
t = update(d1, m1, m2)
275+
else:
276+
t = update(d2, m2, m1)
277+
if t != -1:
278+
return t - 1
279+
return -1
280+
```
281+
* 时间复杂度:$O(n^2)$
282+
* 空间复杂度:$O(n^2)$
283+
284+
---
285+
286+
### 最后
287+
288+
这是我们「刷穿 LeetCode」系列文章的第 `No.934` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
289+
290+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
291+
292+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
293+
294+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
295+

0 commit comments

Comments
 (0)