给我三分钟,带你领略热血江湖中的并查集算法

简介: 给我三分钟,带你领略热血江湖中的并查集算法

一、什么是并查集

并查集被很多OIer认为是最简洁而优雅的数据结构之一,主要用于处理一些不相交集合的合并问题,并支持两种操作:

  • 合并(Union):把两个不相交的集合合并为一个集合。
  • 查询(Find):查询两个元素是否在同一个集合中。

当然,这样的定义让人感觉摸不着头脑,我们来一个样例进行分析。

二、实现并查集

并查集的初始化:

class UnionAndFind() {
    // 自己的大哥是谁
    private int[] parent;
    // 自己的队伍有多少人
    private int[] size;
    // 江湖还有几个派系
    int sets;
    // 初始化
    // 每个人的大哥都是自己
    // 每个队伍的人数都为1
    public UnionAndFind(int N) {
        parent = new int[N];
        size = new int[N];
        help = new int[N];
        sets = N;
        for (int i = 0; i < N; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }
}

并查集的 查询(Find) 方法:这里的时间复杂度可以降低到O(1)级别,不敢相信吧,那就往下看吧

  // 查询a和b在不在一个阵营中
  public boolean isSameSet(int a, int b) {
        return find(a) == find(b);
    }
  // 看一下你的最终大哥是谁
    public int find(int i){
        while(i != parent[i]){
            i = parent[i];
        }
        return i;
    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                

并查集的 合并(Union)方法:先去找到两个参数的最终大哥,小弟少的大哥跟着小弟多的大哥混

  public void union(int a, int b) {
        int A = find(a);
        int B = find(b);
        if (A != B) {
            if (size[A] >= size[B]) {
                size[A] += size[B];
                parent[B] = A;
            } else {
                size[B] += size[A];
                parent[A] = B;
            }
            sets--;
        }
    }

三、真题训练

547. 省份数量

  • 初始化数组,拷贝上述并查集即可
public int findCircleNum(int[][] isConnected) {
        int N = isConnected.length;
        UnionAndFind unionFind = new UnionAndFind(N);
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (isConnected[i][j] == 1 && i != j) {
                    unionFind.union(i, j);
                }
            }
        }
        return unionFind.sets;
    }

3b88c397f83542ab8cfd999ac714ba0b.png

四、路径压缩优化

我们目前的并查集已经可以解决大部分问题,但是,我们能够对目前的并查集版本进行路径压缩优化。

我们可以发现,在我们 find() 方法时,我们会进行一个 while() 循环的查找大哥的操作,这种操作十分的浪费时间,有没有什么办法可以进行下优化呢?

如下所示:

我们可以看到对于6、5、3之类来说,每次进行 find 查询的时候,都需要经过2~3次的循环才能够找到1。

如果我们在进行查找的时候,保存一下值,然后重新挂到1的上面,比如:

我要查询下6,肯定会查询找5、3,这个时候,我们把6、5、3保存到临时数组中,在查询完毕后,将这些数组直接挂到1的下面,这样的话,下次查询会降低循环的次数。


当我们查询的次数远远大于我们的数据量时,这个时候 find() 的操作就已经变成了一个O(1)级别的查询时间。

public int find(int i) {
        int h = 0;
        while (i != parent[i]) {
            help[h++] = i;
            i = parent[i];
        }
        for (h--; h >= 0; h--) {
            parent[help[h]] = i;
        }
        return i;
    }

五、总结

并查集算法通常用在处理一些不交集(Disjoint Sets)的合并及查询问题

力扣也有并查集的 tag 专栏:并查集

大家可以好好体会一下路径压缩的奇妙,通过不断的路径压缩,可以将我们的时间复杂度降低到O(1)


这里可能有小伙伴有疑问,为啥是O(1),你就算挂在1的下面,不也得查询两次嘛


对于时间复杂度来说,只要是常数的次数都可以认定为O(1)的时间复杂度,况且,你也可以使用HashMap来进行存储



相关文章
|
8月前
|
机器学习/深度学习 存储 算法
【算法沉淀】刷题笔记:并查集 带权并查集+实战讲解
【算法沉淀】刷题笔记:并查集 带权并查集+实战讲解
|
8月前
|
算法
并查集的实现【学习算法】
并查集的实现【学习算法】
50 0
|
4月前
|
算法 JavaScript 前端开发
第一个算法项目 | JS实现并查集迷宫算法Demo学习
本文是关于使用JavaScript实现并查集迷宫算法的中国象棋demo的学习记录,包括项目运行方法、知识点梳理、代码赏析以及相关CSS样式表文件的介绍。
第一个算法项目 | JS实现并查集迷宫算法Demo学习
|
3月前
|
存储 算法 Python
火箭般的提升!学会Python并查集,让你的算法能力飞跃新高度!
火箭般的提升!学会Python并查集,让你的算法能力飞跃新高度!
47 1
|
4月前
|
算法 计算机视觉 Python
Python并查集大揭秘:让你在算法界呼风唤雨,秒杀一切复杂场景!
在编程与算法的广袤天地中,总有一些工具如同神兵利器,能够助你一臂之力,在复杂的问题前游刃有余。今天,我们就来深入探讨这样一件神器——Python并查集(Union-Find),看看它是如何让你在算法界呼风唤雨,轻松应对各种复杂场景的。
90 2
|
6月前
|
算法 计算机视觉 Python
Python并查集大揭秘:让你在算法界呼风唤雨,秒杀一切复杂场景!
【7月更文挑战第18天】并查集是Python中解决集合动态合并与查询的利器,常用于复杂问题。例如,在社交网络中快速判断用户是否在同一朋友圈,通过路径压缩优化的`UnionFind`类实现。另外,计算图像中岛屿数量也可借助并查集,将相邻像素合并成集合。并查集的应用显示了其在算法中的高效和灵活性,是提升编程技能的关键工具。
63 2
|
6月前
|
存储 算法 Python
火箭般的提升!学会Python并查集,让你的算法能力飞跃新高度!
【7月更文挑战第17天】并查集,高效解决集合合并查询问题,常用于图的连通性判断。Python实现关键包含查找和合并操作。初始化时,元素各自为集合。查找使用路径压缩优化,合并则可选按秩策略保持平衡。例如,检测无向图环路,遍历边,若并查集发现边两端已在同一集合,则存在环。掌握并查集,提升算法能力,助你在问题解决中一飞冲天!动手实践,成为算法达人!
72 2
|
8月前
|
算法 C++
c++算法学习笔记 (16) 并查集
c++算法学习笔记 (16) 并查集
|
7月前
|
算法
【经典LeetCode算法题目专栏分类】【第9期】深度优先搜索DFS与并查集:括号生成、岛屿问题、扫雷游戏
【经典LeetCode算法题目专栏分类】【第9期】深度优先搜索DFS与并查集:括号生成、岛屿问题、扫雷游戏
|
8月前
|
算法 测试技术
并查集算法
并查集算法

热门文章

最新文章