引入
- 并查集也被称为不相交集数据结构
- 我们来从一个例题说起
有若干个强盗 有若干个线索,每个线索中的两个强盗为同伙(倘若A与B是同伙,B与C是同伙,那么A与C也是同伙),判断有多少个独立的犯罪团伙。
样例数据:
10 9 //10个强盗 9条线索
1 2
3 4
5 2
4 6
2 6
8 7
9 7
1 6
2 4
分析
1.我们可以先确定一个数组,把每个强盗的首领存放在数组里,序号代表第几个强盗,里面存储的数据则代表强盗的首领,先把每个强盗的首领当作是自己
2.然后从线索的第一条进行查找,1 2,就是1号和2号的首领是一个人,我们做一个规定,左边的人权力更大,则2号顶点的首领是1号顶点,改变f[2]的值,f[2]=1
3.第二条线索,3 4,根据规定f[4]=3
4.第三条线索,5 2,根据规定f[2]=5,但是2号强盗本来是1号强盗的人,1号强盗就不愿意了,为什么要抢我的人呢?我们就检测f[2]的boss是不是它自己,如果不是,就让5号与2号强盗的首领谈,让她归顺自己,即f[f[2]]=5,就是f[1]=5,同时现在5号是最大的boss,寻找2号的最大boss时,改变f[2]的值让f[2]=5
5.第四条线索,4 6,根据规定,因为4号强盗的boss不是自己,所以要让f[6]=3
6.第五条线索,2 6,f[2]=5,f[6]=3,根据规定f[3]=5
7.第六条线索,7 11,根据规定f[11]=7
8.第七条线索,8 7,根据规定f[7]=8
9.第八条线索,9 7,根据规则f[8]=9
10.第九条线索,9 11,我们发现9号和11号的首领不是同一个人,因为f[9]=9,f[11]=7,好像不在同一个集团,但是不对吧,根据递归f[11]=7,f[7]=8,f[8]=9,明明是一个,我们就要找到各自的最大首领来判断,要不然就会出现错误
代码实现
#include<stdio.h>
int n,m,sum=0,f[1001]={0};
//初始化数组
void init(){
int i;
for(i=1;i<=n;i++)
f[i]=i;
return;
}
//找爹的递归函数,不停的去找爹
//直到找到祖宗为止,其实就是去找犯罪团伙的最高领导人
//擒贼先擒王原则
int getf(int v){
if(f[v]==v)
return v;
else{
//这里是路径压缩,每次在函数返回的时候,顺带把路上遇到的人的"BOSS"
//改为最后找到的祖宗编号,也就是犯罪团伙的最高领导人编号
f[v]=getf(f[v]);//这里进行了路径压缩
return f[v];
}
}
//合并两子集的函数
void merge(int v,int u){
int t1,t2;//t1、t2分别为v和u的大BOSS
t1=getf(v);
t2=getf(u);
if(t1!=t2)//判断两个结点是否在同一个集合中,即是否为同一个祖先
//靠左原则,左边变成右边的BOSS
f[t2]=t1;
return;
}
int main(){
int i,x,y;
scanf("%d %d",&n,&m);
//初始化
init();
for(i=1;i<=m;i++){
//开始合并犯罪团伙
scanf("%d %d",&x,&y);
merge(x,y);
}
//最后扫描有多少个独立的犯罪团伙
for(i=1;i<=n;i++){
if(f[i]==i)
sum++;
}
printf("%d",sum);
return 0;
}