首页 > 其他分享 >【知识点】集合与并查集

【知识点】集合与并查集

时间:2024-05-22 14:09:26浏览次数:22  
标签:知识点 parent text 查集 节点 集合 find

在前几篇文章当中,我们已经讨论了许多有关数论的知识点了,因此 Macw 决定写几篇数据结构的文章缓一缓。(整天写数论相关的内容容易自闭(bushi))。

今天我们将会围绕一个新的数据结构,并查集(Disjoint Set Union)来展开。

集合与集合的常见操作

在谈论到并查集的时候,首先讨论一个前置知识点,即 集合(Set)的概念。集合是一种无序且不重复的元素集,用数学可以表示为 \(S = \{a, b, c, \dots\}\),其中 \(S\) 是这个集合的名字,\(a, b, c\) 等就是这个集合中的元素。

集合的应用非常的广泛。举一个大家生活中都比较熟悉的例子,运动会报名人数统计。假设目前有两个集合,分别表示参加篮球比赛的选手和参加羽毛球比赛的选手,写作:

\(Basketball = \{\text{Alice}, \text{Bob}, \text{Cindy}, \text{Richard}, \text{Tom}, \text{Fred}\} | \text{参加篮球比赛的选手}\)。
\(Badminton = \{\text{Vincent}, \text{Cindy}, \text{Tom}, \text{Richard}, \text{Selina}, \text{Hans}\} | \text{参加羽毛球比赛的选手}\)。

对于多个集合而言,设 \(A\) 和 \(B\) 是两个集合,常见的有以下操作

  1. 交集(Intersection):求出两个集合中共同包含的元素,写作 \(A \cap B\)。
  2. 并集(Union):求出两个集合中所有的元素并去重,写作 \(A \cup B\)。
  3. 差集(Difference):求出在一个集合 \(A\) 中但不在另一个集合 \(B\) 中的元素,写作 \(A \setminus B\)。

举例而言:

  • 如果我们想要求出既参加了篮球比赛,又参加了羽毛球比赛的同学,可以使用交集操作 \(Basketball \cap Badminton = \{\text{Cindy}, \text{Tom}, \text{Richard}\}\)。表示 Cindy, Tom 和 Richard 三个人既参加了羽毛球比赛,又同时参加了篮球比赛。
  • 同理,如果我们想要求出所有参赛的同学,可以使用并集的操作 \(Basketball \cup Badminton = \{\text{Alice}, \text{Bob}, \text{Cindy}, \text{Richard}, \text{Tom}, \text{Fred}, \text{Vincent}, \text{Selina}, \text{Hans}\}\)。表示这些同学是所有参赛的选手。
  • 如果我们想要求出参加了篮球比赛,但没有参加羽毛球比赛的同学,可以使用差集的操作 \(Basketball \setminus Badminton = \{\text{Alice}, \text{Bob}, \text{Fred}\}\)。表示 Alice, Bob 和 Fred 三人只参加了篮球比赛,没有参加羽毛球比赛。

在介绍完集合的基本操作后,我们将目光着重放在计算 并集 的过程中,这也是今天所要讲的并查集算法的重要组成部分之一。

并查集算法

并查集是一种树形的数据结构,用于处理一些不相交的集合(指的是两个集合的交集为空,即两个集合没有共同的元素),并支持 合并 和 查询 的操作。

  • 查询(Find):查找某个元素所属的集合。通常通过路径压缩(Path Compression)优化,以加快查找速度。
  • 合并(Union):合并两个集合。通常通过按秩合并(Union by Rank)优化,以保证树的高度较低,提高效率。

并查集广泛应用于解决动态连通性问题,如图的连通分量、网络连接、最小生成树等。

并查集代码的实现

在并查集中,每一个集合都可以被看作成一棵树。而集合的标识符也可以被表示为这一棵树的根节点。与此同时,如果在一个场景当中有许多的树,那么这些树将会构成一个森林。

当我们想要找到某一个节点在哪个集合当中,本质上就是在寻找这个节点的根节点编号。若我们想要合并两个集合,本质上就是将一颗树的根节点指向另一棵树的根节点。这样子两棵树就被合并成了一棵树,集合的合并操作也因此完成了。

推荐算法可视化网站:Visualgo.net,各位可以自行访问网站观看并查集算法逻辑的可视化演示。

第一步:定义变量和初始化。

首先,我们需要定义并初始化以个向量/数组,一个用于存储每个元素的父节点。一开始将所有元素的父节点都设置为该节点本身。

vector<int> parent;

void initialize(int n) {
    parent.resize(n);
    for (int i = 0; i < n; ++i) {
        parent[i] = i;
    }
}

第二步:实现查询操作。

接下来,我们实现并查集的 find 操作,用于查找某个元素所属的集合。在查找元素所属的集合时,本质上就是在查找这个集合的父元素。该方法可以通过递归的方式来实现。其中,递归的终止条件就是某一个节点的父节点是该节点本身,代表这个节点是这个集合(树)的根节点。

int find(int x) {
    if (parent[x] != x) {
        return find(parent[x]);
    }
    return parent[x];
}

在此基础上,我们还可以对算法进行时间上的优化,也就是对搜寻路径进行压缩。路径压缩的基本思想是在执行查找操作时,将路径上的每个节点都直接连接到根节点上,从而降低整个树的高度。

int find(int x) {
    if (parent[x] != x) {
        parent[x] = find(parent[x]); // 路径压缩
    }
    return parent[x];
}

路径压缩可以显著减少树的高度,使得查找操作的时间复杂度接近于常数级别,提高了并查集的效率。

第三步:实现合并操作。

然后,我们实现并查集的 union 操作,用于合并两个集合。如果两个元素所指向的父元素是同一个,那么可以说明这两个元素存在于同一个集合当中,不需要进行合并操作。否则就将一个元素添加到另一个集合之中。

void unionSets(int x, int y) {
    int rootX = find(x);
    int rootY = find(y);
    
    if (rootX != rootY) {
        parent[rootY] = rootX;
    }
}

并查集的时间复杂度

并查集的时间复杂度取决于其两个主要操作:查找 和 合并。

查找操作(Find)

在普通的并查集中,如果不进行路径压缩优化,find 操作的时间复杂度与树的高度成正比。最坏情况下,树的高度可以达到 \(O(n)\),其中 \(n\) 是并查集中元素的数量。

但是,在应用了路径压缩优化的情况下,find 操作的均摊时间复杂度接近于常数级别。具体来说,经过路径压缩的并查集的 find 操作的时间复杂度可以表示为 \(O(\alpha(n))\),其中 \(\alpha(n)\) 是阿克曼函数的反函数,增长极其缓慢。对于所有实际应用而言,可以将其约等于为常数时间。

合并操作(Union)

合并操作涉及到查找两个元素所在集合的根节点,并将其中一个根节点连接到另一个根节点上。由于查找过程是常数级别的,而合并两棵树的根节点也是常熟级别的,因此合并操作的时间复杂度也近似于常数时间。

总体复杂度

综上所述,经过路径压缩和按秩合并优化后的并查集,在实际应用中具有非常高效的时间复杂度。对于包含 \(n\) 个元素的并查集,其 findunion 操作的均摊时间复杂度都接近于常数级别,即 \(O(\alpha(n))\)。因此,并查集在解决大规模数据集合动态连通性问题时表现非常出色。

相关引用

  1. GeeksforGeeks. "Introduction to Disjoint Set Data Structure or Union-Find Algorithm." GeeksforGeeks, 12 May 2023, https://www.geeksforgeeks.org/introduction-to-disjoint-set-data-structure-or-union-find-algorithm/.
  2. CP-Algorithms. "Disjoint Set Union." CP-Algorithms, https://cp-algorithms.com/data_structures/disjoint_set_union.html.
  3. USA Computing Olympiad. "DSU." USA Computing Olympiad, https://usaco.guide/gold/dsu?lang=cpp.
  4. Halim, Steven, et al. "Union-Find Disjoint Sets (UFDS) - VisuAlgo." VisuAlgo.net

标签:知识点,parent,text,查集,节点,集合,find
From: https://www.cnblogs.com/Macw07/p/18206110

相关文章

  • 【知识点】拓展欧几里得与中国剩余定理
    在上一篇文章中,我们已经熟知了有关公约数和欧几里得算法的相关事宜。详情参见:欧几里得算法求最大公约数。本文将作为上篇文章内容的一个延续,简要阐述拓展欧几里得算法和中国剩余定理。拓展欧几里得算法拓展欧几里得算法(ExtendedEuclidianAlgorithm),是欧几里得算法的扩展版本,用......
  • 软考知识点整理-程序设计语言
    语义分析阶段:程序编译过程中,执行类型分析和检查语用分析阶段:表示构成语言的各个记号和使用者的关系程序设计语言的基本成分包括数据、运算、控制和传输枚举属于用户定义类型符号表是存储程序源代码中每个标识符和声明的信息动态查找表是查找key的值,若存在则返回位置,不存在就......
  • salesforce零基础学习(一百三十八)零碎知识点小总结(十)
    本篇参考: https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_5level_SOQLqueries.htm&release=250&type=5https://developer.salesforce.com/tools/vscode/en/einstein/einstein-overviewhttps://developer.salesforce.com/tools/vscode/en/user-g......
  • C123【模板】扩展域并查集 P1892 [BOI2003] 团伙
    视频链接:C123【模板】扩展域并查集P1892[BOI2003]团伙_哔哩哔哩_bilibili  P1892[BOI2003]团伙-洛谷|计算机科学教育新生态(luogu.com.cn)//扩展域并查集#include<iostream>#include<cstring>#include<algorithm>usingnamespacestd;intn,m,a,b,......
  • 数据库中了解的知识点:视图、触发器、事务、存储过程、函数、流程控制、索引
    【视图】1什么是视图?2视图就是通过查询得到一张虚拟表,然后保存下来,下次可以直接用3其实视图也是表45为什么要用视图?6如果要频繁的操作一张虚拟表,就可以制作成视图,下次可以直接操作78如何操作9#固定语法10createview......
  • 《Linux程序设计》各章知识点梳理
    《Linux程序设计》各章知识点梳理第1章软件包的管理方式方面,Ubuntu、CentOS的差异如何添加一个新用户?useradduser1什么是Shell?Shell是系统的用户界面,提供了用户与内核进行监护操作的一种接口。它接受用户输入的命令并把它们送去内核去执行。实际上Shell是一个命令......
  • salesforce零基础学习(一百三十七)零碎知识点小总结(九)
    本篇参考: https://help.salesforce.com/s/articleView?id=release-notes.rn_lab_conditional_visibiliy_tab.htm&release=250&type=5https://help.salesforce.com/s/articleView?id=release-notes.rn_automate_flow_builder_automation_lightning_app.htm&release=......
  • Windows Security Baselines(安全基线指南) 是由微软提供的一个安全配置集合,旨在帮助组
    安全基线指南-WindowsSecurity|MicrosoftLearnWindowsSecurityBaselines(安全基线)是由微软提供的一个安全配置集合,旨在帮助组织和管理员快速部署一套推荐的安全设置,以增强Windows操作系统及其组件的安全性。这些基线覆盖了操作系统本身、MicrosoftEdge浏览器、Inter......
  • springboot怎么将List集合数据转成JSON数组
    SpringBoot默认使用Jackson框架将Java对象转换成JSON格式。要转换List集合数据为JSON数组,可以采用以下两种方法:1.使用@ResponseBody注解在SpringBoot中,可以使用@ResponseBody注解标注要返回的List集合数据,让Spring自动将其转换成JSON数组。例如:@GetMapping("/list")@Respo......
  • 使用 Redis Zset 有序集合实现排行榜功能
    一、前言排行榜功能是非常常见的需求,例如商品售卖排行榜单、游戏中的积分排行榜、配送员完单排行榜等。实现排行榜功能需要高效地对大量数据进行排序和查询,如果直接进行数据库查询对应业务排行榜资源开销会非常大,一般会将对应榜单需要的数据做单独存储记录,查询时只要对榜单......