并查集
例题:集合合并
原题描述
一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。
现在要进行 m 个操作,操作共有两种:
M a b,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
Q a b,询问编号为 a 和 b 的两个数是否在同一个集合中;
输入格式
第一行输入整数 n 和 m。
接下来 m 行,每行包含一个操作指令,指令为 M a b 或 Q a b 中的一种。
输出格式
对于每个询问指令 Q a b,都要输出一个结果,如果 a 和 b 在同一集合内,则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1≤n,m≤105
输入样例:
4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4
输出样例:
Yes
No
Yes
参考代码(C++版本)
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int p[N];//维护"祖宗结点"信息的数组,该数组里面只存放父节点
//找到x的祖宗的函数
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);//递归去让x直接找到它的父节点,并直接指向父节点
return p[x];
}
//合并两个子集合的函数
void merge(int a,int b)
{
int t1,t2;
t1 = find(a);
t2 = find(b);
//判断两个是否在一个集合,也就是祖先是否一样
if(t1 != t2) p[t1] = t2;
}
int main()
{
cin >>n >>m;
for(int i = 1;i <= n;i++) p[i] = i;//让自己做自己的祖先,把自己孤立起来
//m次查询
while(m--)
{
char op[2];
int a,b;
scanf("%s %d %d",op,&a,&b);
// if(op[0] == 'M') p[find(a)] = find(b);
if(*op == 'M') merge(a,b);
else
{
if(find(a) == find(b)) puts("Yes");
else puts("No");
}
}
return 0;
}
知识储备 ⭐⭐⭐
什么是并查集
并查集是一种用来管理元素分组情况的数据结构。并查集也称不相交集数据结构。
并查集贯彻的思想是擒贼先擒王,只关注“老大”是谁、“祖宗”是谁。
并查集的作用
并查集可以动态地联通两个点,并且可以非常快速的判断两个点是否联通。并查集可以高效地进行查询元素a和元素b是否属于同一组 和 合并元素a和元素b所在的组操作。不过需要注意并查集虽然可以进行合并操作,但是却无法进行分割操作
并查集的使用
假设存在n个结点,我们先将所有结点的父结点标记为自己;每次要链接结点i和j的时候,我们可将i的父亲标记为j;每次查询两个结点是否相连时,就可以通过查找i和j的祖先最终是否是一个人。
并查集的使用前提
我们先将所有结点的父结点标记为自己。让每一个点最初保持孤立的状态,然后就通过一些后序给的条件,将看起来关系散乱的各个点逐渐合并起来。
并查集的核心🍭🍭🍭
1. 查询元素a和元素b是否属于同一组 --> find()函数:返回祖宗结点的同时压缩路径
这是一个“找父亲”的递归函数,不停的去找父亲。找父亲,找父亲的父亲,找父亲的父亲的父亲… 直到最后找到源头的祖宗为止。
最后返回p[x]实现压缩路径,让x的父结点直接变成祖宗结点。保证让自己可以直接和祖宗产生联系,那么再次查找这个x的时候,就不用逐层逐层的找了。
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
2. 合并元素a和元素b所在的组 --> merge()函数
```
void merge(int a,int b)
{
int t1,t2;
//找到a ,b 各自的祖先
t1 = find(a);
t2 = find(b);
if(t1 != t2) //判断两个是否在一个集合,也就是祖先是否一样
{
//让a的祖先结点中存放b的祖先的信息。现在a也是b的祖先旗下的一枚子民了
p[t1] = t2;
}
}
```
剖析例题⭐⭐⭐
题干要求
题目要让我们实现的
1、将编号为 a 和 b 的两个数所在的集合合并
2、询问编号为 a 和 b 的两个数是否在同一个集合中
分析
对于查询而言,主要是依靠find函数实现,由于上文已经详解,此处不在赘述
对于合并而言,主要是依靠merge函数实现,由于上文已经详解,此处不在赘述
需要注意的点
我们先将所有结点的父结点标记为自己
for(int i = 1;i <= n;i++) p[i] = i;