数据结构之并查集

概念

并查集 (union & find) 是⼀种树型的数据结构,⽤于处理⼀些不交集(Disjoint Sets)的合并及查询问题

两种操作方式:

  • Find: 确定元素属于哪⼀个⼦集。它可以被⽤来确定两个元素是否 属于同⼀子集

  • Union: 将两个⼦集合并成同⼀个集合
    在这里插入图片描述
    在这里插入图片描述
    比如:我们判断 c,f 是否在同一个集合中,以及将上述两个集合合并为一个集合。

  • 在⽣活中的例例⼦子

  1. ⼩弟 —> ⽼大
  2. 帮派识别

时间复杂度

在这里插入图片描述

也就是:经过优化后的代码,时间复杂度为 O(5)

如何代码实现

一般内部使用数组实现

public class UF {
    private int[] roots;

    public UF(int n) {
        roots = new int[n];
        for (int i = 0; i < n; i++) {
            roots[i] = i;
        }
    }

    // 确定元素属于哪⼀个⼦集,返回 集合 的老大
    public int find(int x) {
        if (roots[x] == x) {
            return x;
        }
        return find(roots[x]);
    }

    // 将两个⼦集合并成同⼀个集合
    public void union(int x, int y) {
        int xRoot = find(x);
        int yRoot = find(y);
        // x 集合的老大归属了 y 集合的老大
        roots[xRoot] = yRoot;
    }

    public static void main(String[] args) {
        UF uf = new UF(100);
        uf.union(1, 2);
        uf.union(2, 3);

        uf.union(4, 5);

        System.out.println(uf.find(1));
        System.out.println(uf.find(2));
        System.out.println(uf.find(3));

        System.out.println(uf.find(4));
        System.out.println(uf.find(5));
    }
}

输出结果是:

3
3
3
5
5

Process finished with exit code 0

上述构造出来的结构是:
在这里插入图片描述

两种优化手段

根据 Rank 进行优化

看图,上面两个集合,合并的话会存在下面的两种方式,很明显,第二种合并方式要好的多。具体的实现就是:保存一个Rank(树高),在每次合并的时候,总是树低的往树高的合并
在这里插入图片描述
示例 PY 代码:
在这里插入图片描述

路径压缩优化

还是看图,很明显,我们在使用并查集时,并不关注集合本身到底长什么样子,而关注的是 什么元素在什么集合中,所以就可以做出如下优化。
在这里插入图片描述
核心逻辑列在这里:
在这里插入图片描述

public class UFUpgrade {
    private int[] roots;
    // 即:树高
    private int[] rank;


    public UFUpgrade(int n) {
        roots = new int[n];
        rank = new int[n];
        for (int i = 0; i < n; i++) {
            roots[i] = i;
            rank[i] = 1;
        }
    }

    // 确定元素属于哪⼀个⼦集,返回 集合 的老大
    public int find(int x) {
        while (x != roots[x]) {
            roots[x] = roots[roots[x]];
            rank[x] -= 1;
            x = roots[x];
        }
        return x;
    }

    // 将两个⼦集合并成同⼀个集合
    public void union(int x, int y) {
        int xRoot = find(x);
        int yRoot = find(y);
        // 相交, 跳过
        if (xRoot == yRoot) {
            return;
        }
        // 在每次合并的时候,总是树低的往树高的合并
        if (rank[xRoot] < rank[yRoot]) {
            // x 集合的老大归属了 y 集合的老大
            roots[xRoot] = yRoot;
            rank[xRoot] += 1;

        } else {
            roots[yRoot] = xRoot;
            rank[yRoot] += 1;
        }
    }

    public static void main(String[] args) {
        UFUpgrade uf = new UFUpgrade(10);
        uf.union(1, 2);
        uf.union(2, 3);

        uf.union(4, 5);


        for (int i = 0; i < 5; i++) {
            System.out.print(uf.roots[i]);
        }
        System.out.println();

        System.out.println(uf.find(1));
        System.out.println(uf.find(2));
        System.out.println(uf.find(3));

        System.out.println(uf.find(4));
        System.out.println(uf.find(5));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值