Skip to content

Commit 522d366

Browse files
author
oushihao
committed
feat(number-of-islands): 递归 | 迭代 | 并查集
1 parent 36f7320 commit 522d366

File tree

7 files changed

+552
-8
lines changed

7 files changed

+552
-8
lines changed

assets/data/category.json

+17
Original file line numberDiff line numberDiff line change
@@ -1598,6 +1598,12 @@
15981598
"path": "../../problemset/binary-tree-right-side-view/README.md",
15991599
"difficulty": "中等"
16001600
},
1601+
{
1602+
"id": 200,
1603+
"title": "岛屿数量",
1604+
"path": "../../problemset/number-of-islands/README.md",
1605+
"difficulty": "中等"
1606+
},
16011607
{
16021608
"id": 589,
16031609
"title": "N 叉树的前序遍历",
@@ -1976,5 +1982,16 @@
19761982
"difficulty": "中等"
19771983
}
19781984
]
1985+
},
1986+
{
1987+
"label": "并查集",
1988+
"problems": [
1989+
{
1990+
"id": 200,
1991+
"title": "岛屿数量",
1992+
"path": "../../problemset/number-of-islands/README.md",
1993+
"difficulty": "中等"
1994+
}
1995+
]
19791996
}
19801997
]

assets/data/problems.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -1749,6 +1749,16 @@
17491749
"url": "https://leetcode-cn.com/problems/binary-tree-right-side-view/",
17501750
"path": "../../problemset/binary-tree-right-side-view/README.md"
17511751
},
1752+
{
1753+
"id": 200,
1754+
"title": {
1755+
"cn": "岛屿数量",
1756+
"en": "number-of-islands"
1757+
},
1758+
"difficulty": "中等",
1759+
"url": "https://leetcode-cn.com/problems/number-of-islands/",
1760+
"path": "../../problemset/number-of-islands/README.md"
1761+
},
17521762
{
17531763
"id": 258,
17541764
"title": {
@@ -2199,4 +2209,4 @@
21992209
"url": "https://leetcode-cn.com/problems/sum-of-subarray-ranges/",
22002210
"path": "../../problemset/sum-of-subarray-ranges/README.md"
22012211
}
2202-
]
2212+
]

assets/docs/CATEGORY.md

+7
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@
320320
| 145. [二叉树的后序遍历](../../problemset/binary-tree-postorder-traversal/README.md) | 简单 |
321321
| 173. [二叉搜索树迭代器](../../problemset/binary-search-tree-iterator/README.md) | 中等 |
322322
| 199. [二叉树的右视图](../../problemset/binary-tree-right-side-view/README.md) | 中等 |
323+
| 200. [岛屿数量](../../problemset/number-of-islands/README.md) | 中等 |
323324
| 589. [N 叉树的前序遍历](../../problemset/n-ary-tree-preorder-traversal/README.md) | 简单 |
324325
| 590. [N 叉树的后序遍历](../../problemset/n-ary-tree-postorder-traversal/README.md) | 简单 |
325326
| 838. [推多米诺](../../problemset/push-dominoes/README.md) | 中等 |
@@ -412,3 +413,9 @@
412413
| 393. [UTF-8 编码验证](../../problemset/utf-8-validation/README.md) | 中等 |
413414
| 1763. [最长的美好子字符串](../../problemset/longest-nice-substring/README.md) | 简单 |
414415
| 2044. [统计按位或能得到最大值的子集数目](../../problemset/count-number-of-maximum-bitwise-or-subsets/README.md) | 中等 |
416+
417+
## 并查集
418+
419+
| 题目 | 难度 |
420+
| ---- | ---- |
421+
| 200. [岛屿数量](../../problemset/number-of-islands/README.md) | 中等 |

assets/docs/PROBLEMS.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@
184184

185185
[92. 反转链表 II](../../problemset/reverse-linked-list-2/README.md)
186186

187-
[93. 复原 IP 地址](../../problemset/restore-ip-addresses/README.md)
187+
[93. 复原IP地址](../../problemset/restore-ip-addresses/README.md)
188188

189189
[94. 二叉树的中序遍历](../../problemset/binary-tree-inorder-traversal/README.md)
190190

@@ -290,7 +290,7 @@
290290

291291
[145. 二叉树的后序遍历](../../problemset/binary-tree-postorder-traversal/README.md)
292292

293-
[146. LRU 缓存](../../problemset/lru-cache/README.md)
293+
[146. LRU缓存](../../problemset/lru-cache/README.md)
294294

295295
[147. 对链表进行插入排序](../../problemset/insertion-sort-list/README.md)
296296

@@ -322,7 +322,7 @@
322322

323323
[167. 两数之和 II - 输入有序数组](../../problemset/two-sum-ii-input-array-is-sorted/README.md)
324324

325-
[168. Excel 表列名称](../../problemset/excel-sheet-column-title/README.md)
325+
[168. Excel表列名称](../../problemset/excel-sheet-column-title/README.md)
326326

327327
[169. 多数元素](../../problemset/majority-element/README.md)
328328

@@ -336,19 +336,21 @@
336336

337337
[179. 最大数](../../problemset/largest-number/README.md)
338338

339-
[187. 重复的 DNA 序列](../../problemset/repeated-dna-sequences/README.md)
339+
[187. 重复的DNA序列](../../problemset/repeated-dna-sequences/README.md)
340340

341341
[188. 买卖股票的最佳时机 IV](../../problemset/best-time-to-buy-and-sell-stock-4/README.md)
342342

343343
[189. 轮转数组](../../problemset/rotate-array/README.md)
344344

345345
[190. 颠倒二进制位](../../problemset/reverse-bits/README.md)
346346

347-
[191. 位 1 的个数](../../problemset/number-of-1-bits/README.md)
347+
[191. 位1的个数](../../problemset/number-of-1-bits/README.md)
348348

349349
[198. 打家劫舍](../../problemset/house-robber/README.md)
350350

351-
[199. 二叉树的右视图](../../problemset/binary-tree-right-side-view/README.md)
351+
[199. 二叉树的右视图](../../problemset/binary-tree-right-side-view//README.md)
352+
353+
[200. 岛屿数量](../../problemset/number-of-islands/README.md)
352354

353355
[258. 各位相加](../../problemset/add-digits/README.md)
354356

@@ -438,4 +440,4 @@
438440

439441
[2100. 适合打劫银行的日子](../../problemset/find-good-days-to-rob-the-bank/README.md)
440442

441-
[2104. 子数组范围和](../../problemset/sum-of-subarray-ranges/README.md)
443+
[2104. 子数组范围和](../../problemset/sum-of-subarray-ranges/README.md)
+262
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
# 岛屿数量
2+
3+
> 难度:中等
4+
>
5+
> https://leetcode-cn.com/problems/number-of-islands/
6+
7+
## 题目
8+
9+
给你一个由  `'1'`(陆地)和 `'0'`(水)组成的的二维网格,请你计算网格中岛屿的数
10+
量。
11+
12+
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
13+
14+
此外,你可以假设该网格的四条边均被水包围。
15+
16+
### 示例
17+
18+
#### 示例 1:
19+
20+
```
21+
输入:grid = [
22+
["1","1","1","1","0"],
23+
["1","1","0","1","0"],
24+
["1","1","0","0","0"],
25+
["0","0","0","0","0"]
26+
]
27+
输出:1
28+
```
29+
30+
#### 示例 2:
31+
32+
```
33+
输入:grid = [
34+
["1","1","0","0","0"],
35+
["1","1","0","0","0"],
36+
["0","0","1","0","0"],
37+
["0","0","0","1","1"]
38+
]
39+
输出:3
40+
```
41+
42+
## 解题
43+
44+
### 深度优先遍历
45+
46+
```typescript
47+
/**
48+
* 深度优先遍历
49+
* @desc 时间复杂度 O(NM) 空间复杂度 O(NM)
50+
* @param grid
51+
* @returns
52+
*/
53+
export function numIslands(grid: string[][]): number {
54+
if (grid.length === 0 || grid[0].length === 0) return 0;
55+
56+
const m = grid.length;
57+
const n = grid[0].length;
58+
let result = 0;
59+
60+
for (let i = 0; i < m; i++) {
61+
for (let j = 0; j < n; j++) {
62+
if (grid[i][j] !== '1') continue;
63+
result++;
64+
dfs(grid, m, n, i, j);
65+
}
66+
}
67+
68+
return result;
69+
70+
function dfs(
71+
grid: string[][],
72+
m: number,
73+
n: number,
74+
i: number,
75+
j: number
76+
): void {
77+
if (i < 0 || j < 0 || i >= m || j >= n || grid[i][j] === '0') return;
78+
79+
grid[i][j] = '0';
80+
dfs(grid, m, n, i + 1, j);
81+
dfs(grid, m, n, i - 1, j);
82+
dfs(grid, m, n, i, j + 1);
83+
dfs(grid, m, n, i, j - 1);
84+
}
85+
}
86+
```
87+
88+
### 广度优先遍历
89+
90+
```typescript
91+
/**
92+
* 广度优先遍历
93+
* @desc 时间复杂度 O(NM) 空间复杂度 O(NM)
94+
* @param grid
95+
* @returns
96+
*/
97+
export function numIslands2(grid: string[][]): number {
98+
if (grid.length === 0 || grid[0].length === 0) return 0;
99+
100+
const m = grid.length;
101+
const n = grid[0].length;
102+
let result = 0;
103+
104+
// 生成队列key
105+
const generateKey = (i: number, j: number): number =>
106+
i * n + j; /* j 恒小于 n */
107+
108+
for (let i = 0; i < m; i++) {
109+
for (let j = 0; j < n; j++) {
110+
if (grid[i][j] !== '1') continue;
111+
result++;
112+
grid[i][j] = '0';
113+
114+
const queue = [generateKey(i, j)];
115+
116+
while (queue.length) {
117+
const key = queue.pop()!;
118+
const row = (key / n) >> 0;
119+
const col = key % n;
120+
console.log(row, col);
121+
if (row - 1 >= 0 && grid[row - 1][col] === '1') {
122+
queue.unshift(generateKey(row - 1, col));
123+
grid[row - 1][col] = '0';
124+
}
125+
if (row + 1 < m && grid[row + 1][col] === '1') {
126+
queue.unshift(generateKey(row + 1, col));
127+
grid[row + 1][col] = '0';
128+
}
129+
if (col - 1 >= 0 && grid[row][col - 1] === '1') {
130+
queue.unshift(generateKey(row, col - 1));
131+
grid[row][col - 1] = '0';
132+
}
133+
if (col + 1 < n && grid[row][col + 1] === '1') {
134+
queue.unshift(generateKey(row, col + 1));
135+
grid[row][col + 1] = '0';
136+
}
137+
}
138+
}
139+
}
140+
141+
return result;
142+
}
143+
```
144+
145+
### 并查集
146+
147+
```typescript
148+
/**
149+
* 并查集
150+
* @desc 时间复杂度 O(NM) 空间复杂度 O(NM)
151+
* @param grid
152+
* @returns
153+
*/
154+
export function numIslands3(grid: string[][]): number {
155+
if (grid.length === 0 || grid[0].length === 0) return 0;
156+
157+
const unionFind = new UnionFind(grid);
158+
const { m, n } = unionFind;
159+
160+
for (let i = 0; i < m; i++) {
161+
for (let j = 0; j < n; j++) {
162+
if (grid[i][j] === '1') {
163+
grid[i][j] = '0';
164+
if (i - 1 >= 0 && grid[i - 1][j] === '1') {
165+
unionFind.union(
166+
unionFind.generateKey(i, j),
167+
unionFind.generateKey(i - 1, j)
168+
);
169+
}
170+
if (i + 1 < m && grid[i + 1][j] === '1') {
171+
unionFind.union(
172+
unionFind.generateKey(i, j),
173+
unionFind.generateKey(i + 1, j)
174+
);
175+
}
176+
if (j - 1 >= 0 && grid[i][j - 1] === '1') {
177+
unionFind.union(
178+
unionFind.generateKey(i, j),
179+
unionFind.generateKey(i, j - 1)
180+
);
181+
}
182+
if (j + 1 < n && grid[i][j + 1] === '1') {
183+
unionFind.union(
184+
unionFind.generateKey(i, j),
185+
unionFind.generateKey(i, j + 1)
186+
);
187+
}
188+
}
189+
}
190+
}
191+
192+
return unionFind.count;
193+
}
194+
195+
class UnionFind {
196+
count: number; // 记录item为1的个数
197+
parent: number[]; // index为每个item的唯一key,value为它们合并后的根位置
198+
rank: number[]; // 记录每个根位置的合并数量
199+
m: number;
200+
n: number;
201+
202+
constructor(grid: string[][]) {
203+
// 初始化
204+
this.count = 0;
205+
const m = (this.m = grid.length);
206+
const n = (this.n = grid[0].length);
207+
this.parent = new Array(m * n).fill(-1);
208+
this.rank = new Array(m * n).fill(0);
209+
210+
for (let i = 0; i < m; i++) {
211+
for (let j = 0; j < n; j++) {
212+
const key = this.generateKey(i, j);
213+
if (grid[i][j] === '1') {
214+
this.parent[key] = key;
215+
this.count++;
216+
}
217+
}
218+
}
219+
}
220+
221+
/**
222+
* 生成唯一keys
223+
* @param i
224+
* @param j
225+
* @returns
226+
*/
227+
generateKey(i: number, j: number): number {
228+
return i * this.n + j;
229+
}
230+
231+
find(i: number): number {
232+
if (this.parent[i] !== i) this.parent[i] = this.find(this.parent[i]);
233+
return this.parent[i];
234+
}
235+
236+
/**
237+
* 合并操作
238+
* @param x
239+
* @param y
240+
*/
241+
union(x: number, y: number) {
242+
// 找到x,y的根位置
243+
// 这里可以确保x,y的item都为1
244+
const xRoot = this.find(x);
245+
const yRoot = this.find(y);
246+
247+
// xRoot === yRoot 代表已经合并了
248+
if (xRoot === yRoot) return;
249+
250+
// 合并操作
251+
if (this.rank[xRoot] > this.rank[yRoot]) {
252+
this.parent[yRoot] = xRoot;
253+
} else if (this.rank[xRoot] < this.rank[yRoot]) {
254+
this.parent[xRoot] = yRoot;
255+
} else {
256+
this.parent[yRoot] = xRoot;
257+
this.rank[xRoot] += 1;
258+
}
259+
this.count--;
260+
}
261+
}
262+
```

0 commit comments

Comments
 (0)