lotus

贵有恒何必三更眠五更起 最无益只怕一日曝十日寒

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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;
    }
}

  

 

3. 总结

posted on 2023-05-02 19:27  白露~  阅读(16)  评论(0编辑  收藏  举报