Skip to content

Commit 82a55ad

Browse files
authored
feat: update solutions to lc problem: No.0200 (#4893)
See #4772 Co-authored-by: zhirshya
1 parent 7b7af5c commit 82a55ad

File tree

8 files changed

+185
-214
lines changed

8 files changed

+185
-214
lines changed

solution/0200-0299/0200.Number of Islands/README.md

Lines changed: 64 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,11 @@ tags:
6969

7070
<!-- solution:start -->
7171

72-
### 方法一:Flood fill 算法
72+
### 方法一:DFS
7373

74-
Flood fill 算法是从一个区域中提取若干个连通的点与其他相邻区域区分开(或分别染成不同颜色)的经典算法。因为其思路类似洪水从一个区域扩散到所有能到达的区域而得名
74+
我们可以使用深度优先搜索(DFS)来遍历每个岛屿。遍历网格中的每个单元格 $(i, j)$,如果该单元格的值为 '1',则说明我们找到了一个新的岛屿。我们可以从该单元格开始进行 DFS,将与之相连的所有陆地单元格的值都标记为 '0',以避免重复计数。每次找到一个新的岛屿时,我们将岛屿数量加 1
7575

76-
最简单的实现方法是采用 DFS 的递归方法,也可以采用 BFS 的迭代来实现。
77-
78-
时间复杂度 $O(m\times n)$,空间复杂度 $O(m\times n)$。其中 $m$ 和 $n$ 分别为网格的行数和列数。
76+
时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别为网格的行数和列数。
7977

8078
<!-- tabs:start -->
8179

@@ -150,7 +148,7 @@ public:
150148
int n = grid[0].size();
151149
int ans = 0;
152150
int dirs[5] = {-1, 0, 1, 0, -1};
153-
function<void(int, int)> dfs = [&](int i, int j) {
151+
auto dfs = [&](this auto&& dfs, int i, int j) -> void {
154152
grid[i][j] = '0';
155153
for (int k = 0; k < 4; ++k) {
156154
int x = i + dirs[k], y = j + dirs[k + 1];
@@ -208,24 +206,28 @@ function numIslands(grid: string[][]): number {
208206
const m = grid.length;
209207
const n = grid[0].length;
210208
let ans = 0;
209+
const dirs = [-1, 0, 1, 0, -1];
210+
211211
const dfs = (i: number, j: number) => {
212-
if (grid[i]?.[j] !== '1') {
213-
return;
214-
}
215212
grid[i][j] = '0';
216-
dfs(i + 1, j);
217-
dfs(i - 1, j);
218-
dfs(i, j + 1);
219-
dfs(i, j - 1);
213+
for (let k = 0; k < 4; ++k) {
214+
const x = i + dirs[k];
215+
const y = j + dirs[k + 1];
216+
if (grid[x]?.[y] === '1') {
217+
dfs(x, y);
218+
}
219+
}
220220
};
221+
221222
for (let i = 0; i < m; ++i) {
222223
for (let j = 0; j < n; ++j) {
223224
if (grid[i][j] === '1') {
224225
dfs(i, j);
225-
++ans;
226+
ans++;
226227
}
227228
}
228229
}
230+
229231
return ans;
230232
}
231233
```
@@ -271,44 +273,37 @@ impl Solution {
271273
#### C#
272274

273275
```cs
274-
using System;
275-
using System.Collections.Generic;
276-
using System.Linq;
277-
278276
public class Solution {
279-
public int NumIslands(char[][] grid)
280-
{
281-
var queue = new Queue<Tuple<int, int>>();
282-
var lenI = grid.Length;
283-
var lenJ = lenI == 0 ? 0 : grid[0].Length;
284-
var paths = new int[,] { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
285-
var result = 0;
286-
for (var i = 0; i < lenI; ++i)
287-
{
288-
for (var j = 0; j < lenJ; ++j)
289-
{
290-
if (grid[i][j] == '1')
277+
public int NumIslands(char[][] grid) {
278+
int m = grid.Length;
279+
int n = grid[0].Length;
280+
int ans = 0;
281+
int[] dirs = { -1, 0, 1, 0, -1 };
282+
283+
void Dfs(int i, int j) {
284+
grid[i][j] = '0';
285+
for (int k = 0; k < 4; ++k) {
286+
int x = i + dirs[k];
287+
int y = j + dirs[k + 1];
288+
if (x >= 0 && x < m &&
289+
y >= 0 && y < n &&
290+
grid[x][y] == '1')
291291
{
292-
++result;
293-
grid[i][j] = '0';
294-
queue.Enqueue(Tuple.Create(i, j));
295-
while (queue.Any())
296-
{
297-
var position = queue.Dequeue();
298-
for (var k = 0; k < 4; ++k)
299-
{
300-
var next = Tuple.Create(position.Item1 + paths[k, 0], position.Item2 + paths[k, 1]);
301-
if (next.Item1 >= 0 && next.Item1 < lenI && next.Item2 >= 0 && next.Item2 < lenJ && grid[next.Item1][next.Item2] == '1')
302-
{
303-
grid[next.Item1][next.Item2] = '0';
304-
queue.Enqueue(next);
305-
}
306-
}
307-
}
292+
Dfs(x, y);
308293
}
309294
}
310295
}
311-
return result;
296+
297+
for (int i = 0; i < m; ++i) {
298+
for (int j = 0; j < n; ++j) {
299+
if (grid[i][j] == '1') {
300+
Dfs(i, j);
301+
ans++;
302+
}
303+
}
304+
}
305+
306+
return ans;
312307
}
313308
}
314309
```
@@ -319,44 +314,18 @@ public class Solution {
319314

320315
<!-- solution:start -->
321316

322-
### 方法二:并查集
323-
324-
并查集是一种树形的数据结构,顾名思义,它用于处理一些不交集的**合并****查询**问题。 它支持两种操作:
325-
326-
1. 查找(Find):确定某个元素处于哪个子集,单次操作时间复杂度 $O(\alpha(n))$
327-
1. 合并(Union):将两个子集合并成一个集合,单次操作时间复杂度 $O(\alpha(n))$
328-
329-
其中 $\alpha$ 为阿克曼函数的反函数,其增长极其缓慢,也就是说其单次操作的平均运行时间可以认为是一个很小的常数。
330-
331-
以下是并查集的常用模板,需要熟练掌握。其中:
332-
333-
- `n` 表示节点数
334-
- `p` 存储每个点的父节点,初始时每个点的父节点都是自己
335-
- `size` 只有当节点是祖宗节点时才有意义,表示祖宗节点所在集合中,点的数量
336-
- `find(x)` 函数用于查找 $x$ 所在集合的祖宗节点
337-
- `union(a, b)` 函数用于合并 $a$ 和 $b$ 所在的集合
338-
339-
```python
340-
p = list(range(n))
341-
size = [1] * n
317+
### 方法二:BFS
342318

319+
我们也可以使用广度优先搜索(BFS)来遍历每个岛屿。遍历网格中的每个单元格 $(i, j)$,如果该单元格的值为 '1',则说明我们找到了一个新的岛屿。我们可以从该单元格开始进行 BFS,将与之相连的所有陆地单元格的值都标记为 '0',以避免重复计数。每次找到一个新的岛屿时,我们将岛屿数量加 1。
343320

344-
def find(x):
345-
if p[x] != x:
346-
# 路径压缩
347-
p[x] = find(p[x])
348-
return p[x]
321+
BFS 的具体过程如下:
349322

323+
1. 将起始单元格 $(i, j)$ 入队,并将其值标记为 '0'。
324+
2. 当队列不为空时,执行以下操作:
325+
- 出队一个单元格 $p$。
326+
- 遍历 $p$ 的四个相邻单元格 $(x, y)$,如果 $(x, y)$ 在网格范围内且值为 '1',则将其入队并将其值标记为 '0'。
350327

351-
def union(a, b):
352-
pa, pb = find(a), find(b)
353-
if pa == pb:
354-
return
355-
p[pa] = pb
356-
size[pb] += size[pa]
357-
```
358-
359-
时间复杂度 $O(m\times n\times \alpha(m\times n))$。其中 $m$ 和 $n$ 分别为网格的行数和列数。
328+
时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别为网格的行数和列数。
360329

361330
<!-- tabs:start -->
362331

@@ -441,11 +410,10 @@ public:
441410
int n = grid[0].size();
442411
int ans = 0;
443412
int dirs[5] = {-1, 0, 1, 0, -1};
444-
function<void(int, int)> bfs = [&](int i, int j) {
413+
auto bfs = [&](int i, int j) -> void {
445414
grid[i][j] = '0';
446415
queue<pair<int, int>> q;
447416
q.push({i, j});
448-
vector<int> dirs = {-1, 0, 1, 0, -1};
449417
while (!q.empty()) {
450418
auto [a, b] = q.front();
451419
q.pop();
@@ -513,22 +481,21 @@ function numIslands(grid: string[][]): number {
513481
const m = grid.length;
514482
const n = grid[0].length;
515483
let ans = 0;
516-
function bfs(i, j) {
484+
const bfs = (i: number, j: number) => {
517485
grid[i][j] = '0';
518-
let q = [[i, j]];
486+
const q = [[i, j]];
519487
const dirs = [-1, 0, 1, 0, -1];
520-
while (q.length) {
521-
[i, j] = q.shift();
488+
for (const [i, j] of q) {
522489
for (let k = 0; k < 4; ++k) {
523490
const x = i + dirs[k];
524491
const y = j + dirs[k + 1];
525-
if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1') {
492+
if (grid[x]?.[y] == '1') {
526493
q.push([x, y]);
527494
grid[x][y] = '0';
528495
}
529496
}
530497
}
531-
}
498+
};
532499
for (let i = 0; i < m; ++i) {
533500
for (let j = 0; j < n; ++j) {
534501
if (grid[i][j] == '1') {
@@ -592,7 +559,11 @@ impl Solution {
592559

593560
<!-- solution:start -->
594561

595-
### 方法三
562+
### 方法三: 并查集
563+
564+
我们可以使用并查集(Union-Find)来解决这个问题。遍历网格中的每个单元格 $(i, j)$,如果该单元格的值为 '1',则将其与相邻的陆地单元格进行合并。最后,我们统计并查集中不同根节点的数量,即为岛屿的数量。
565+
566+
时间复杂度 $O(m \times n \times \log (m \times n))$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别为网格的行数和列数。
596567

597568
<!-- tabs:start -->
598569

@@ -758,11 +729,8 @@ func numIslands(grid [][]byte) int {
758729
function numIslands(grid: string[][]): number {
759730
const m = grid.length;
760731
const n = grid[0].length;
761-
let p = [];
762-
for (let i = 0; i < m * n; ++i) {
763-
p.push(i);
764-
}
765-
function find(x) {
732+
const p: number[] = Array.from({ length: m * n }, (_, i) => i);
733+
function find(x: number): number {
766734
if (p[x] != x) {
767735
p[x] = find(p[x]);
768736
}
@@ -775,7 +743,7 @@ function numIslands(grid: string[][]): number {
775743
for (let k = 0; k < 2; ++k) {
776744
const x = i + dirs[k];
777745
const y = j + dirs[k + 1];
778-
if (x < m && y < n && grid[x][y] == '1') {
746+
if (grid[x]?.[y] == '1') {
779747
p[find(i * n + j)] = find(x * n + y);
780748
}
781749
}

0 commit comments

Comments
 (0)