1. 题目
读题
https://leetcode.cn/problems/number-of-islands/submissions/
考查点
这道题的考察点是:
- 如何使用并查集这种数据结构来解决一些关于集合的问题,例如判断两个元素是否属于同一个集合,或者统计有多少个不相交的集合。
- 如何实现并查集的两个基本操作:查找和合并,并且使用路径压缩的方法来优化查找的效率。
- 如何把二维网格中的元素映射到一维数组中,以便使用并查集的数据结构。
- 如何遍历二维网格中的元素,并且根据相邻关系来合并属于同一个岛屿的元素。
- 如何统计最终有多少个不同的岛屿,即根节点的数量。
2. 解法
思路
使用并查集的思路是:把每个陆地格子看作一个节点,把相邻的陆地节点合并为一个集合,最后统计有多少个不同的集合即可。具体步骤如下:
- 初始化一个一维数组 parent,用来存储每个节点的父节点,初始时每个节点的父节点都是自己。
- 遍历二维网格,对于每个陆地格子,找到其在一维数组中对应的索引,然后检查其上下左右四个方向是否也是陆地,如果是,则调用 merge 函数将两个节点合并为一个集合。
- merge 函数的作用是:找到两个节点各自的根节点(即最终的父节点),如果根节点不同,则将其中一个根节点指向另一个根节点,表示两个集合合并为一个。
- find 函数的作用是:找到一个节点的根节点,并且在查找过程中进行路径压缩,即将沿途的节点都指向根节点,以提高后续查找的效率。
- 最后遍历一维数组 parent,统计有多少个节点的父节点是自己,即有多少个根节点,这就是岛屿的数量。
代码逻辑
- 第一步:定义一个一维数组 parent,用来存储每个节点的父节点,初始时每个节点的父节点都是自己。
- 第二步:定义一个 find 函数,用来查找一个节点的根节点,并进行路径压缩。具体做法是:
- 如果一个节点的父节点是自己,就返回自己。
- 如果一个节点的父节点不是自己,就递归地查找它的父节点的根节点,并且在返回过程中把它的父节点更新为根节点,这样就可以把沿途的节点都指向根节点,提高后续查找的效率。
- 第三步:定义一个 merge 函数,用来合并两个节点所在的集合。具体做法是:
- 先分别查找两个节点的根节点。
- 如果根节点不同,就把其中一个根节点指向另一个根节点,表示两个集合合并为一个。
- 第四步:定义一个 numIslands 函数,用来计算岛屿的数量。具体做法是:
- 如果二维网格为空或者没有元素,就返回 0。
- 获取二维网格的行数和列数,并初始化一维数组 parent 的长度为行数乘以列数。
- 遍历二维网格,对于每个格子:
- 如果当前格子是陆地,就计算它在一维数组中对应的索引。
- 然后检查它的上下左右四个方向是否也是陆地,如果是,就调用 merge 函数将两个节点合并为一个集合。
- 最后遍历一维数组 parent,统计有多少个不同的根节点,并且对应的格子是陆地,这就是岛屿的数量。
具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | 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; } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2019-05-02 设计模式:迭代器模式(Iterator)
2019-05-02 设计模式:解释器模式(Interpreter)
2019-05-02 设计模式:命令模式(Command)
2019-05-02 设计模式:职责链模式(Chain of Responsibility)
2019-05-02 设计模式:单例模式(单例模式)
2019-05-02 设计模式:原型模式(Prototype)
2019-05-02 浅析java的浅拷贝和深拷贝