并查集

并查集

基本介绍

数据结构一共分为四大类:集合、线性表、树、图,本文的数据结构就是第一种结构集合。

假设,一个集合即是一个小团伙,每个小团伙都有一个老大,每个团伙成员认识另一个成员,而团队比较大不可能每个成员都互相认识吧,但是他们认识的人去找别的认识的人中最终都可以找到老大,这些人就构成了一个集合。那么这个老大我们称之为祖宗,每个人认识的那个人就是它的“父亲“。

有点类似于树的结构,这种寻找一般都是单向的,但是也可以增加数据域保存”孩子“的信息(一般不用)。类似于树,我们就可以像操作树那样,用递归的方式实现寻找(当然也可以非递归)。

核心思想

一个节点一般包含两个信息域,一是自身的信息,二是父亲的信息。

常见用法:定义一个数组,令数组角标为数据的地址,单个数组元素的一个数据域就可以用来存储“父亲“的角标,以实现元素之间的连通。

判断两个元素是否在同一个集合,只需要不断找“父亲”指导找到“祖宗“,判断“祖宗“是否相同即可。


图 方框内是数组角标,圆内是单个数组元素的数据域。图中即为两个集合。

代码实现

初始化

并查集在使用前一定要初始化,让每个节点自己做自己的父亲,因为最开始每个元素独立自己是一个集合。

 

void init(int len) {
    for (int i = 0; i < len; i++)
        family[i].parent = i;
    return;
}

找“祖宗“

单纯找

  核心原理就是不断的通过数据域的信息去找“父亲”,直到找到一个自己是自己“父亲”的节点即“祖宗”。这个过程可以用递归也可以非递归实现。

1.     递归

 

int find(int aim) {
    if (family[aim].parent != aim)
        aim = find(family[aim].parent);
    return aim;
}

2.     非递归

 

int find(int x) {
    while (x != fa[x])
        x = fa[x];
    return x;
}


路径压缩

对于有些题目,实际路径不会对解题有影响,就可以采用路径压缩,其目的就是减少find使用的时间。如上例子,“6”号位找祖宗要找三次,采用路径压缩只需要找一次即可。

 

int find(int x, int *fa){
    if(fa[x] != x) 
        fa[x] = find(fa[x], fa);//查找+路径压缩,如果没有祖先就回溯
    return fa[x];
}


合并

合并操作就是检查两个元素是否是同一个集合,如果不是就需要合并在同一个集合。对于谁做谁的父节点一般情况下没有任何关系,因为并查集只是表述集合关系。但是在某些对路径有要求的题目下,就要考虑谁做谁的父节点。

 

void merge(int a, int b, int *fa)
{
    a = find(a, fa);
    b = find(b, fa);
    if (a != b)
        fa[a] = b; // 如果两个元素不相等就让一个元素成为另一个元素的父节点
    return;
}


练习题

  https://www.luogu.com.cn/problem/P1551 P1551 亲戚

  https://www.luogu.com.cn/problem/P1536 P1536 村村通

  https://www.luogu.com.cn/problem/P1525 P1525 [NOIP2010 提高组]关押罪(种类并查集)

  https://www.luogu.com.cn/problem/P1621 P1621 集合

  https://www.luogu.com.cn/problem/P1892 P1892 [BOI2003]团伙

  https://www.luogu.com.cn/problem/P1955 P1955 [NOI2015] 程序自动分析(这个有趣)

  https://www.luogu.com.cn/problem/P2814 P2814 家谱(这个简单,不过字符串处理一下)

  https://vjudge.net/contest/445444 这个并查集题单还是挺有趣的

  http://acm.hdu.edu.cn/showproblem.php?pid=3038 How Many Answers Are Wrong(这是一个带权并查集,感兴趣的可以试试)

我的题解

  P1551 亲戚 - Kirk~~ - 博客园 (cnblogs.com)

  P1536 村村通 - Kirk~~ - 博客园 (cnblogs.com)

  P1525 [NOIP2010 提高组] 关押罪犯 - Kirk~~ - 博客园 (cnblogs.com)

  P2814 家谱 - Kirk~~ - 博客园 (cnblogs.com)

posted @ 2021-07-12 12:01  Kirk~~  阅读(53)  评论(0编辑  收藏  举报