并查集模板代码
通过y总学习的就会发现,这个模板很常用会与后面的图论与密切的联系。
因此并查集时面试以及竞赛常考的算法之一!!!
用法:实现将两个集合合并、询问两个元素是否在同一个集合、判断连通块节点个数(时间复杂度近乎O(1),注意:这里是近乎)。个人认为:学习算法重要的不是背会模板,而是弄清楚每个算法的用途,提高算法题敏锐度。
算法大致实现内容(个人笔记):每个集合的编号就是根节点的编号,在这里每个节点存储的是他的父节点。例如:p[x]表示x的父节点。
用法的实现:
判断每个节点属于哪个集合:(注意在这个的集合在后面的题当中出现的关键词大多是类别、连通块等)判断该系欸但一直向上找根节点找到树根即可找到集合编号while(p[x]!=x)x=p[x];------后面会有路径压缩优化
如何判断树根:if(p[x]==x)只有根节点具有这样的性质
如何将两个集合进行合并:将某个集合作为另一个集合的儿子(加一条边)p[find(a)]=find(b);
优化(路径压缩):一旦搜索x的根节点,该路径所有的点都指向父节点表示已搜
#include <iostream>
using namespace std;
int n,m;
int p[1000010];//并查集必须有father数组
//find操作:返回x的祖宗节点+路径压缩(优化)
int find(int x)
{
if(p[x]!=x)p[x]=find(p[x]);
//return p[x];//x=p[x]递归指向父节点
}
int main(){
cin>>n>>m;
//初始化:让所有的节点的父节点都指向自己
for(int i=1;i<=n;i++)p[i]=i;//注意下标从1开始
while(m--)
{
char op[2];
int a,b;
cin>>op>>a>>b;
if(op[0]=='M')p[find(a)]=find(b);
else {
if(find(a)==find(b))cout<<"Yes";
else cout<<"No";
cout<<endl;
}
}
return 0;
}
处理并查集集合:求连通块节点个数
#include <iostream>
using namespace std;
int n,m;
int p[1000010],s[1000010];//size数组表示每个集合的大小
int find(int x)
{
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
{
p[i]=i;
s[i]=1;
}
while(m--)
{
char op[5];
int a,b;
cin>>op;
if(op[0]=='C')//表示合并,修改对应根节点的size
{
cin>>a>>b;
if(find(a)==find(b))continue;//a b已经在一个集合中
s[find(b)]+=s[find(a)];
p[find(a)]=find(b);
}
else if (op[1]=='1'){//判断是否在同一个集合
cin>>a>>b;
if(find(a)==find(b))cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
else {
cin>>a;
cout<<s[find(a)]<<endl;
}
}
return 0;
}
只需要加size数组进行维护每个集合(连通块)的节点个数,只需要在每次合并时进行更新。
注意:只有根节点的size数组的有效的