并查集

左手的ㄟ右手 2022-12-05 00:55 16阅读 0赞

并查集

  • 什么是并查集
  • 并查集的结构
    • 初始化
    • 合并
    • 查询
    • 实现
      • 初始化
      • 查询树的根节点
      • 合并
      • 查询
  • 再谈并查集
    • 再优化
    • 并查集的时间复杂度

什么是并查集

并查集是一种十分简单的数据结构,它可以用来描述元素的分组情况。
并查集可以十分高效地进行“并”和“查”两个操作,但无法进行分割操作。
其中:

  • 并:合并a,b两个元素所在的组;
  • 查:查询a,b两个元素是否属于同一组;

并查集的本质是一棵棵树的合并,以及树的遍历。

并查集的结构

并查集是一种树形结构(并非二叉树),使用并查集后,多数情况下会有不止一棵树,即一个森林。

初始化

在初始状态时,所有元素均和自己且只和自己是一组,这是毋庸置疑的。

并查集作为一个树形结构,我们需要有一个根节点用来描述一棵树,这里,我们定义一个“par数组”来描述这个根节点。

在初始化时,需要设定:

  1. par[i] = i;

初始化

合并

现在给出信息:

1和4一组
2和3一组
1和3一组
5和6一组

对于给出的四条信息,我们依次进行操作:

  1. 由于1和4一组,我们约定:左侧节点作为根节点,因此,我们将par[4]更新为1;
  2. 由于2和3一组,根据靠左原则,将par[3]更新为2;
  3. 当遇见“1和3一组”这条信息时,我们发现2和3一组,如果贸然将par[3]更新为1,就不满足2和3一组的条件;为同时保证2和3一组的条件,我们需要找到它的根节点并将其根节点与另一节点合并(即2,并将2和1合并),此时将par[2]更新为1,如此便可以满足2和3一组的同时1和3也处于同一组。
  4. 由于5和6一组,根据靠左原则,将par[6]更新为5;

(在第三步中,在找到其根节点的过程中,可以将其路径上的所有节点par直接更新为根节点,这个操作称为路径压缩,此示例没有体现)
如此,我们可以得到如下图所示的森林:
合并操作

查询

为了查询两个节点是否属于同一组,我们需要沿着树向上走,以此来查询包含这个元素的根节点是谁;如果两个节点走到了同一个根节点,那么我们就可以知道他们属于同一组。
假设现在查询3和4是否处于同一组中,我们需要:

  1. 找到4的根节点:1
  2. 找到3的根节点:1
    2.1.在此过程中,3先找到其父节点2,发现2并非根节点,再向上寻找发现1为根节点,此过程中可以将2和3的par都更新为1
  3. 可以发现3和4的根节点都是1,说明他们属于一个集合

实现

初始化

  1. void init (int n){
  2. for (int i = 0;i < n; i++){
  3. par[i] = i;
  4. }
  5. }

查询树的根节点

在合并和查询时,我们都需要找到其根节点:

  • 如果此节点是单个节点,即par[x] == x,就说明它是根节点
  • 否则,判断par[x]是否为根节点,直至找到根节点为止

如此可以得到如下函数:

  1. int getf(int x)
  2. {
  3. if(par[x] == x){
  4. return x;
  5. }
  6. else {
  7. return par[x] = getf(par[x]);//这里用到了路径压缩操作
  8. }
  9. }

或者可以简写成如下形式:

  1. int getf(int x){
  2. return par[x] == x ? x : par[x] = getf(par[x]);
  3. }

合并

  1. void unite(int x,int y)
  2. {
  3. x=getf(x);
  4. y=getf(y);
  5. if(x!=y){
  6. par[y]=x;
  7. }
  8. }

查询

  1. bool query(int x, int y){
  2. return getf(x) == getf(y);
  3. }

再谈并查集

再优化

在对树的学习中,我们知道,当一棵树退化成链时,其效率是非常低的,因此我们要对树的层数进行平衡。
在这里插入图片描述

如此一来,我们需要额外多维护一个树的高度(rank)。
合并时,如果两棵树的rank不同,则不遵循靠左原则,从rank小的树向rank大的树连边
这样,我们可以得到最终的初始化和合并函数:

  1. void init (int n){
  2. for (int i = 0;i < n; i++){
  3. par[i] = i;
  4. rank[i] = 0;
  5. }
  6. }
  7. void unite(int x,int y){
  8. x = getf(x);
  9. y = getf(y);
  10. if (x == y){
  11. return;
  12. }
  13. if (rank[x] < rank[y]){
  14. par[x] = y;
  15. }
  16. else{
  17. par[y] = x;
  18. if (rank[x] == rank[y]){
  19. rank[x]++;
  20. }
  21. }
  22. }

并查集的时间复杂度

在避免了树的退化的同时加入路径压缩操作,并查集的效率非常高,其均摊复杂度为:
Θ ( α ( n ) ) \Theta(\alpha(n)) Θ(α(n)),其中 α ( n ) \alpha(n) α(n)为阿克曼函数的反函数,此效率比 Θ ( log ⁡ ( n ) ) \Theta(\log(n)) Θ(log(n))还要高。

发表评论

表情:
评论列表 (有 0 条评论,16人围观)

还没有评论,来说两句吧...

相关阅读

    相关

    并查集JAVA版框架 并查集是一种用来管理元素分组情况的数据结构。 并查集可以高效地进行如下操作: \--查询元素a和元素b是否属于同一组

    相关

    这个文章是几年前水acm的时候转的, 当时也不知道作者是谁, 要是有人知道的话说一下吧 并查集是我暑假从高手那里学到的一招,觉得真是太精妙的设计了。以前我无法解决的

    相关

    > 题目 > 某学校近期要组织全校同学出去参加某项活动,由于人数众多,学校决定让同学们自行组队,以小组为单位进行活动。假设学校一共n个同学,每个同学有一个唯一的数字作为标签

    相关

    森林: 森林是由若干棵互不相交的树组成,两棵树分别独立,没有交集 ![20181112082744488.png][] 并查集: 并查集的结构和森林十分相似,是

    相关

    来看一个实例,[杭电1232畅通工程][1232] 首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性

    相关

    > 并查集是一种树形的数据结构,用于集合的合并。 与C++STL相比的优势: 1. 可以同时维护多个集合 2. 快速的合并两个集合 \\现实: 用数组实现。fat

    相关

    例题: ![输入样例][20190805122001677.png] 输入样例,第一行为一个整数n,代表有n台电脑,编号1-n;接下来进行一系列操作,当输入‘c’时,判

    相关

    一、算法解释 用于解决一些有N个元素的集合应用问题。 1、将每个元素初始化为自身单独成为一个集合。用p\[i\]的值表示该元素所在集合。 ![这里写图片描述][S

    相关

    并查集的作用就是快速判断两个元素是否在同一个集合中,快速将两个集合合并 基本模板 include <iostream> include <a