@@ -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-
278276public 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 {
758729function 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