zoukankan      html  css  js  c++  java
  • 【40讲系列14】并查集

    一、理论

    并查集的定义:

    并查集是一种树型的数据结构,用于处理一些不交集的合并和查询问题。一般用数组实现。

    Find:确定元素属于哪一个子集,它可以被用来确定两个元素是否属于同一个子集。

    Union:将两个子集合并成同一个集合。

    并查集的优化:

    优化1: 降低rank,提高查询效率。合并时要考虑rank(即树结构的深度,在并查集里称为rank),优先让rank低的合并到rank高的。

    优化2:路径压缩。不需要创建额外的内存,rank。 用的更多。

    二、典型例题

    ☆☆①:二维网格中的小岛数量统计问题(LC200)

    Note:【岛屿问题】是一个系列的网格问题,LC463->岛屿的周长、 LC695->岛屿的最大面积 、LC827->最大人工岛

    此类问题参考题解:岛屿类问题的通用解法、DFS 遍历框架

    方法1:DFS。一般DFS通常是在树或图结构上进行的,岛屿系列问题是网格DFS的典型代表。

    方法2:BFS

    方法3:并查集

    代码1<DFS>

    class Solution {
        public int numIslands(char[][] grid) {
            int count = 0;
            if (grid == null || grid.length == 0) return count;
            for (int i = 0; i < grid.length; i++) {
                for (int j = 0; j < grid[0].length; j++) {
                    if (grid[i][j] == '1') {
                        dfs(grid, i, j);
                        count++;
                    }
                }
            }
            return count;
        }
        public void dfs(char[][] grid, int r, int c) {
            // 超出网格范围。 "先污染后治理"->先往四个方向走一步再说,如果发现走出了网格范围再赶紧返回。
            if (r < 0 || r >= grid.length || c < 0 || c >= grid[0].length) {
                return;
            }
            // 如果这个格子不是岛屿,返回。有两种情况:'0'海洋格子。'2'陆地格子(已遍历过)
            if (grid[r][c] != '1') {
                return;
            }
            grid[r][c] = '2'; // 避免重复遍历"兜圈子",标记已遍历过的格子
            dfs(grid,r-1,c);
            dfs(grid,r+1,c);
            dfs(grid,r,c-1);
            dfs(grid,r,c+1);
        }
    }

    ②:计算矩阵中的朋友圈总数(LC547)

    方法1:图的DFS。给定的矩阵看成图的邻接矩阵,问题可以变成求无向图连通块的个数。

    方法2:图的BFS

    方法3:并查集

    代码1<DFS>

    class Solution {
        public int findCircleNum(int[][] M) {
            /**
             * 方法1:图的DFS
             * 首先选择一个节点,访问任一相邻的节点。然后再访问这一节点的任一相邻节点。
             * 这样不断遍历到没有未访问的相邻节点时,回溯到之前的节点进行访问。
             */
            int count = 0;
            if (M == null || M.length == 0) return count;
            boolean[] visited = new boolean[M.length];
            for (int i = 0; i < M.length; i++) {
                if (!visited[i]) {
                    dfs(M, visited, i);
                    count++;
                }
            }
            return count;
        }
        public void dfs(int[][] M, boolean[] visited, int i) {
            visited[i] = true;
            for (int j = 0; j < M.length; j++) {
                if (!visited[j] && M[i][j] == 1) {
                    dfs(M,visited,j);
                }
            }
        }
    }

     代码3<并查集>

    并查集讲解

    本题解法

    class Solution {
        public int findCircleNum(int[][] isConnected) {
            // 方法2:并查集, 底层基于数组实现
            int n = isConnected.length;
            UnionFind uf = new UnionFind(n);
            for (int i = 0; i < n; i++) {
                // 将当前节点与其邻接点进行合并
                for (int j = i + 1; j < n; j++) {
                    if (isConnected[i][j] == 1) {
                        uf.union(i, j);
                    }
                }
            }
            return uf.size;
        }
    }
    class UnionFind{
        int[] roots;
        int size;
        public UnionFind(int n) {
            roots = new int[n];
            for (int i = 0; i < n; i++) {
                // 初始化时指向自己
                roots[i] = i;
            }
            size = n;
        }
        public int find(int i) {
            if (i == roots[i]) {
                return i;
            }
            return roots[i] = find(roots[i]);
        }
        public void union(int p, int q) {
            int pRoot = find(p);
            int qRoot = find(q);
            if (pRoot != qRoot) {
                roots[pRoot] = qRoot;
                size --;
            }
        }
    }
  • 相关阅读:
    Scala Ant Tasks
    Git挂钩
    读写文件
    DC10用CSS定位控制网页布局
    table设置colspan属性,列宽显示错位解决方法
    ATM和购物商城-错题集
    python 函数参数多种传递方法
    python 函数 初学
    python 集合 gather
    元组 字体高亮 购物车练习
  • 原文地址:https://www.cnblogs.com/HuangYJ/p/14057889.html
Copyright © 2011-2022 走看看