只实现联通功能
地址:
描述:
思想:![](https://img-blog.csdnimg.cn/20210901091421175.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5byg5p2O5rWp,size_20,color_FFFFFF,t_70,g_se,x_16)
//普通版的find()函数,返回祖宗节点
int find(int x) {
if (x == p[x]) //如果x是祖先,则返回
return x;
else
return find(p[x]); //如果x不是祖先,那么就去问x的爸爸它的爸爸是不是祖宗节点
}
//路径压缩的find函数,返回祖宗节点
int find(int x) {
if (x != p[x]) // x不是自身的父亲,即x不是该集合的代表
//把x的父节点改为集合的祖宗节点,即把x路径上的点的父节点都变为祖宗节点(路径压缩)
//find(p[x])的作用是通过不断递归找到祖宗节点赋值给x的父节点
p[x]=find(p[x]);
return p[x];//返回x的父节点下标,即该集合的祖宗节点
}
为什么不写p[x]=find(x)写成find(p[x])?
find(p[x])的作用是通过不断递归找到祖宗节点赋值给x的父节点
代码:
#include <iostream>
using namespace std;
int n,m;//n是节点数,m是要进行的操作数
const int N=1e5+10;
//存放节点的父节点
int p[N];
//返回祖宗节点+路径压缩
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;//它的父亲节点==它自己,每个节点都是它所在集合的根节点
for(int i=0;i<m;i++){
//指令的形式 M a b
char op;
int a,b;
cin>>op>>a>>b;
//op=='M'集合合并
if(op=='M'){
//两个点不在同一集合
if(find(a)!=find(b)) p[find(a)]=find(b);
}
//查询两点是否在同一集合
if(op=='Q'){
if(find(a)==find(b)) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}
return 0;
}
实现统计集合(联通块)个数功能
地址:
描述:
思想:
整体思路和上面差不多,只是在上面联通集合的基础上加入了size[]来方便记录集合中节点数
总体上只在这两处发生改动
代码:
#include <iostream>
#include <cstring>
using namespace std;
int n,m;//n是节点数,m是要进行的操作数
const int N=1e5+10;
//存放节点的父节点
int p[N];
//size[i]数组只在i节点为集合的根节点是有效,且代表i节点所在集合节点数
int Size[N];
//返回祖宗节点+路径压缩
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;//它的父亲节点==它自己,每个节点都是它所在集合的根节点
Size[i]=1;
}
for(int i=0;i<m;i++){
//指令的形式 Q1 a b Q2 a C a b
string op;
int a,b;
cin>>op;
//op=='C'集合合并
if(op=="C"){
cin>>a>>b;
//两个点不在同一集合,这里是把a所在节点加到b里面
if(find(a)!=find(b)) {
Size[find(b)]+=Size[find(a)];
p[find(a)]=find(b);
}
}
//查询两点是否在同一集合
if(op=="Q1"){
cin>>a>>b;
if(find(a)==find(b)) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
//查询集合中点的个数
if(op=="Q2"){
cin>>a;
//找到a节点所在集合的根节点下标
cout<<Size[find(a)]<<endl;
}
}
return 0;
}