并查集

Posted yangfei629

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集相关的知识,希望对你有一定的参考价值。

一、场景


 

并查集多数是用来解决连通性问题的

如至少修建几条路可以使所有路口可以相通

把互通的路口当做一个群组,那问题就是有几个这样的群组

如有N个群组 那至少修N-1条路 就可以把所有路口相连。

 

二、使用


 

并查集分两步

1、合并

合并就是把相连的多个节点划到一个群组中

可以定义数组parent[] 表示对应节点的父节点索引。

 

这样不断查询节点A的父节点,可以找到组先节点(父节点就是本身的节点)

所谓相连节点就是设置同一个组先节点

 

2、查询

查询两个节点是否相连,只要判断各自的组先节点是否相同,如果相同,就是相连的

在查询的过程中 可以路径压缩(节点的父节点直接指向组先节点),使结构扁平化,提升查询效率

技术图片

 

 

 

 

三、示例


 

如leecode第952题:按公因数计算最大组件大小

给定一个由不同正整数的组成的非空数组 A,考虑下面的图:
有 A.length 个节点,按从 A[0] 到 A[A.length - 1] 标记;
只有当 A[i] 和 A[j] 共用一个大于 1 的公因数时,A[i] 和 A[j] 之间才有一条边。
返回图中最大连通组件的大小。
 
示例 1:
输入:[4,6,15,35]
输出:4
示例 2:
输入:[20,50,9,63]
输出:2

 

这个问题就是一个并查集的问题,只要知道哪些数是一组的,再求出最大一组的元素个数即可

解题如下

class Solution {
    private class UnionFindUtil {

        // 对应父节点位置
        private int[] parent;

        // 初始化 每个元素的父节点索引==自身索引
        public UnionFindUtil(int n) {
            parent = new int[n];
            for (int i = 0; i < n; i++) {
                parent[i] = i;
            }
        }

        // 查找
        public int find(int x) {
            if (parent[x] == x) {
                return x;
            }

            // 路径压缩
            parent[x] = find(parent[x]);

            return parent[x];
        }

        // 合并
        public void union(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);

            if (rootX != rootY) {
                parent[rootX] = rootY;
            }
        }
    }

    public  int largestComponentSize(int[] A) {
        int maxVal = 0;
        for (int num : A) {
            maxVal = Math.max(maxVal, num);
        }

        // 使用质数来判断是否两连
        UnionFindUtil unionFind = new UnionFindUtil(maxVal + 1);

        for (int num : A) {
            double upBound = Math.sqrt(num);
            for (int i = 2; i <= upBound; i++) {
                if (num % i == 0) {
                    unionFind.union(num, i);
                    unionFind.union(num, num / i);
                }
            }
        }

        // 找出最大者
        int[] cnt = new int[maxVal + 1];
        int res = 0;
        for (int num : A) {
            int root = UnionFindUtil.find(num);
            // 统计组先节点出现的次数 每次加一
            cnt[root]++;
            res = Math.max(res, cnt[root]);
        }
        return res;
    }

}

 

 

 


 

以上是关于并查集的主要内容,如果未能解决你的问题,请参考以下文章

想要学会并查集吗?看我四十行代码实现它

树--12---并查集

笔记并查集---无向图处理代码模板及类型题

并查集

力扣 每日一题 886. 可能的二分法难度:中等,rating: 1794(并查集 / 拆点优化的扩展域并查集)

并查集