并查集
将两个集合合并
询问两个元素是否在一个集合当中
基本原理: 每个集合用一颗树来表示。树根的编号就是整个集合的编号。每个结点存储它的父结点,p[x]表示x的父结点。
问题1: 如何判断树根 if (p[x] == x)
问题2: 如何求x的集合编号: while (p[x] != x) x = p[x];
问题3: 如何合并两个集合: px 是 x 的集合编号,py 是 y 的集合编号。p[x] = y。
路径压缩: find 函数不仅有找祖宗的功能,还把这个查找路径上所有节点直接变成了祖宗节点的孩子
题目

代码
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int p[N];
int n,m;
//路径压缩: find 函数不仅有找祖宗的功能,还把这个查找路径上所有节点直接变成了祖宗节点的孩子
//路径压缩前: 1->2->3->4->5 需要通过父结点一级一级往上找,最后找到祖宗结点
//路径压缩后: 1->5 2->5 3->5 4->5 不需要一级一级找,直接就能找到祖宗结点
int find(int x)
{
if(p[x]!=x)
p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
//最开始每个数各自在一个集合中,编号是1~n
//每个数的祖宗结点就是自己
for(int i=1;i<=n;i++)
p[i]=i;
while(m--)
{
string op;
int a,b;
cin>>op>>a>>b;
if(op=="M")
p[find(a)]=p[find(b)];
else
{
if(find(a)==find(b))
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
}
return 0;
}
样例讲解
int n=4,m=5
idx: 1 2 3 4
p[N]: 1 2 3 4
M 1 2(将1和2所在的集合合并)
a=1,b=2
p[find(a)]=p[find(b)];
执行过程: 1. find(b): 调用find函数查找 2 的祖宗结点。p[2]=2,2的祖宗结点是2
2. p[find(b)]=p[2]=2
3. find(a): 调用find函数查找 1 的祖宗结点。p[1]=1,1的祖宗结点是1
4. p[find(a)]=p[1]
5. 赋值: p[1]=p[2]=2
idx: 1 2 3 4
p[N]: 2 2 3 4
M 3 4(将3和4所在的集合合并,即把3的祖宗结点改成4的祖宗结点,或把4的祖宗结点改成3的祖宗结点)
a=3,b=4
p[find(a)]=p[find(b)];
执行过程: 1. find(b): 调用find函数查找 4 的祖宗结点。p[4]=4,4的祖宗结点是4
2. p[find(b)]=p[4]=4
3. find(a): 调用find函数查找 3 的祖宗结点。p[3]=3,3的祖宗结点是3
4. p[find(a)]=p[3]
5. 赋值: p[3]=p[4]=4
idx: 1 2 3 4
p[N]: 2 2 4 4
Q 1 2(查询1和2是否在同一个集合里,即判断1和2的祖宗结点是否一致)
a=1,b=2
if(find(a)==find(b));
执行过程: 1. find(b): 调用find函数查找 2 的祖宗结点。p[2]=2,2的祖宗结点是2
2. p[find(b)]=p[2]=2
3. find(a): 调用find函数查找 1 的祖宗结点。
1. p[1]!=1,p[1]=find(p[1])
2. 现在x=p[1]=2,p[p[1]]=p[2]=2=x=2,return p[x](也就是p[2]=2)
3. p[1]=2,1的祖宗结点是2
4. 判断: 2 == 2
5. 1和2在同一个集合里
Q 1 3(查询1和3是否在同一个集合里,即判断1和3的祖宗结点是否一致)
a=1,b=3
if(find(a)==find(b));
执行过程: 1. find(b): 调用find函数查找 3 的祖宗结点。
1. p[3]!=3,p[3]=find(p[3])
2. 现在x=p[3]=4,p[p[3]]=p[4]=4=x=4,return p[x](也就是p[4]=4)
3. p[3]=4,3的祖宗结点是4
2. p[find(b)]=p[3]=4
3. find(a): 调用find函数查找 1 的祖宗结点。p[1]=2,1的祖宗结点是2
4. 判断: 2 != 4
5. 1和3不在同一个集合里
Q 3 4(查询3和4是否在同一个集合里,即判断3和4的祖宗结点是否一致)
a=3,b=4
if(find(a)==find(b));
执行过程: 1. find(b): 调用find函数查找 4 的祖宗结点。p[4]=4,4的祖宗结点是4
2. p[find(b)]=p[4]=4
3. find(a): 调用find函数查找 3 的祖宗结点。
1. p[3]!=3,p[3]=find(p[3])
2. 现在x=p[3]=4,p[p[3]]=p[4]=4=x=4,return p[x](也就是p[4]=4)
3. p[3]=4,3的祖宗结点是4
4. 判断: 4 == 4
5. 3和4在同一个集合里
文章详细介绍了并查集数据结构的基本原理,包括树状表示、路径压缩技术以及合并和查询集合的操作。通过实例演示了如何使用find函数进行集合查找和合并,展示了路径压缩如何提高查询效率。


被折叠的 条评论
为什么被折叠?



