1. 题目

读题
https://leetcode.cn/problems/number-of-islands/submissions/
考查点
这道题的考察点是:
- 如何使用并查集这种数据结构来解决一些关于集合的问题,例如判断两个元素是否属于同一个集合,或者统计有多少个不相交的集合。
- 如何实现并查集的两个基本操作:查找和合并,并且使用路径压缩的方法来优化查找的效率。
- 如何把二维网格中的元素映射到一维数组中,以便使用并查集的数据结构。
- 如何遍历二维网格中的元素,并且根据相邻关系来合并属于同一个岛屿的元素。
- 如何统计最终有多少个不同的岛屿,即根节点的数量。
2. 解法
思路
使用并查集的思路是:把每个陆地格子看作一个节点,把相邻的陆地节点合并为一个集合,最后统计有多少个不同的集合即可。具体步骤如下:
- 初始化一个一维数组 parent,用来存储每个节点的父节点,初始时每个节点的父节点都是自己。
- 遍历二维网格,对于每个陆地格子,找到其在一维数组中对应的索引,然后检查其上下左右四个方向是否也是陆地,如果是,则调用 merge 函数将两个节点合并为一个集合。
- merge 函数的作用是:找到两个节点各自的根节点(即最终的父节点),如果根节点不同,则将其中一个根节点指向另一个根节点,表示两个集合合并为一个。
- find 函数的作用是:找到一个节点的根节点,并且在查找过程中进行路径压缩,即将沿途的节点都指向根节点,以提高后续查找的效率。
- 最后遍历一维数组 parent,统计有多少个节点的父节点是自己,即有多少个根节点,这就是岛屿的数量。
代码逻辑
- 第一步:定义一个一维数组 parent,用来存储每个节点的父节点,初始时每个节点的父节点都是自己。
- 第二步:定义一个 find 函数,用来查找一个节点的根节点,并进行路径压缩。具体做法是:
- 如果一个节点的父节点是自己,就返回自己。
- 如果一个节点的父节点不是自己,就递归地查找它的父节点的根节点,并且在返回过程中把它的父节点更新为根节点,这样就可以把沿途的节点都指向根节点,提高后续查找的效率。
- 第三步:定义一个 merge 函数,用来合并两个节点所在的集合。具体做法是:
- 先分别查找两个节点的根节点。
- 如果根节点不同,就把其中一个根节点指向另一个根节点,表示两个集合合并为一个。
- 第四步:定义一个 numIslands 函数,用来计算岛屿的数量。具体做法是:
- 如果二维网格为空或者没有元素,就返回 0。
- 获取二维网格的行数和列数,并初始化一维数组 parent 的长度为行数乘以列数。
- 遍历二维网格,对于每个格子:
- 如果当前格子是陆地,就计算它在一维数组中对应的索引。
- 然后检查它的上下左右四个方向是否也是陆地,如果是,就调用 merge 函数将两个节点合并为一个集合。
- 最后遍历一维数组 parent,统计有多少个不同的根节点,并且对应的格子是陆地,这就是岛屿的数量。
具体实现
class Solution {
// 定义一个一维数组存储每个节点的父节点
private int[] parent;
// 查找一个节点的根节点,并进行路径压缩
private int find(int x) {
int fa = parent[x];
if (fa != x) {
fa = find(fa);
// 把沿途的节点都指向根节点
parent[x] = fa;
}
return fa;
}
// 合并两个节点所在的集合
private void merge(int x, int y) {
int fx = find(x);
int fy = find(y);
if (fx != fy) {
// 把其中一个根节点指向另一个根节点
parent[fx] = fy;
}
}
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) return 0;
int m = grid.length; // 行数
int n = grid[0].length; // 列数
// 初始化一维数组,长度为 m * n
parent = new int[m * n];
for (int i = 0; i < m * n; i++) {
// 初始时每个节点都是自己的父节点
parent[i] = i;
}
// 遍历二维网格
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
// 如果当前格子是陆地
if (grid[i][j] == '1') {
// 计算其在一维数组中对应的索引
int index = i * n + j;
// 检查其上下左右四个方向
// 上方
if (i > 0 && grid[i - 1][j] == '1') {
// 如果上方也是陆地,合并两个节点
merge(index, index - n);
}
// 下方
if (i < m - 1 && grid[i + 1][j] == '1') {
// 如果下方也是陆地,合并两个节点
merge(index, index + n);
}
// 左方
if (j > 0 && grid[i][j - 1] == '1') {
// 如果左方也是陆地,合并两个节点
merge(index, index - 1);
}
// 右方
if (j < n - 1 && grid[i][j + 1] == '1') {
// 如果右方也是陆地,合并两个节点
merge(index, index + 1);
}
}
}
}
// 统计岛屿数量,即根节点的数量
int count = 0;
for (int i = 0; i < m * n; i++) {
// 如果一个节点的父节点是自己,说明它是一个根节点
if (parent[i] == i) {
// 如果对应的格子是陆地,说明它是一个岛屿的根节点
if (grid[i / n][i % n] == '1') {
count++;
}
}
}
return count;
}
}
浙公网安备 33010602011771号