# 并查集详解 (转)

4 2 1 3 4 3

int pre[1000 ];

int find(int x)                                                                                                         //查找根节点

int r=x;

while ( pre[r ] != r )                                                                                              //返回根节点 r

r=pre[r ];

int i=x , j ;

while( i != r )                                                                                                        //路径压缩

{

j = pre[ i ]; // 在改变上级之前用临时变量  j 记录下他的值

pre[ i ]= r ; //把上级改为根节点

i=j;

}

return r ;

}

void join(int x,int y)                                                                                                    //判断x y是否连通，

//如果已经连通，就不用管了 //如果不连通，就把它们所在的连通分支合并起,

{

int fx=find(x),fy=find(y);

if(fx!=fy)

pre[fx ]=fy;

}

http://i3.6.cn/cvbnm/6f/ec/f4/1e9cfcd3def64d26ed1a49d72c1f6db9.jpg

int find(int x)                                                                  //查找我（x）的掌门

{

int r=x;                                                                       //委托 r 去找掌门

while (pre[r ]!=r)                                                        //如果r的上级不是r自己（也就是说找到的大侠他不是掌门 = =）

r=pre[r ] ;                                                                   // r 就接着找他的上级，直到找到掌门为止。

return  r ;                                                                   //掌门驾到~~~

}

void join(int x,int y)                                                                   //我想让虚竹和周芷若做朋友

{

int fx=find(x),fy=find(y);                                                       //虚竹的老大是玄慈，芷若MM的老大是灭绝

if(fx!=fy)                                                                               //玄慈和灭绝显然不是同一个人

pre[fx ]=fy;                                                                           //方丈只好委委屈屈地当了师太的手下啦

}

http://i3.6.cn/cvbnm/60/98/92/745b3eac68181e4ee1fa8d1b8bca38bc.jpg

#include<iostream>
using namespace std;

int  pre[1050];
bool t[1050];               //t 用于标记独立块的根结点

int Find(int x)
{
int r=x;
while(r!=pre[r])
r=pre[r];

int i=x,j;
while(pre[i]!=r)
{
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}

void mix(int x,int y)
{
int fx=Find(x),fy=Find(y);
if(fx!=fy)
{
pre[fy]=fx;
}
}

int main()
{
int N,M,a,b,i,j,ans;
while(scanf("%d%d",&N,&M)&&N)
{
for(i=1;i<=N;i++)          //初始化
pre[i]=i;

for(i=1;i<=M;i++)          //吸收并整理数据
{
scanf("%d%d",&a,&b);
mix(a,b);
}

memset(t,0,sizeof(t));
for(i=1;i<=N;i++)          //标记根结点
{
t[Find(i)]=1;
}
for(ans=0,i=1;i<=N;i++)
if(t[i])
ans++;

printf("%d\n",ans-1);

}
return 0;


#include int pre[1000 ];

int find(int x)

{

int r=x;

while (pre[r ]!=r)

r=pre[r ];

int i=x; int j;

while(i!=r)

{

j=pre[i ];

pre[i ]=r;

i=j;

}

return r;

}

int main()

{

int n,m,p1,p2,i,total,f1,f2;

while(scanf("%d",&n) && n)         //读入n，如果n为0，结束

{                                                    //刚开始的时候，有n个城镇，一条路都没有 //那么要修n-1条路才能把它们连起来

total=n-1;

//每个点互相独立，自成一个集合，从1编号到n //所以每个点的上级都是自己

for(i=1;i<=n;i++) { pre[i ]=i; }                //共有m条路

scanf("%d",&m); while(m--)

{ //下面这段代码，其实就是join函数，只是稍作改动以适应题目要求

//每读入一条路，看它的端点p1，p2是否已经在一个连通分支里了

scanf("%d %d",&p1,&p2);

f1=find(p1);

f2=find(p2);

//如果是不连通的，那么把这两个分支连起来

//分支的总数就减少了1，还需建的路也就减了1

if(f1!=f2)

{

pre[f2 ]=f1;

total--;

}

//如果两点已经连通了，那么这条路只是在图上增加了一个环 //对连通性没有任何影响，无视掉

}

//最后输出还要修的路条数

printf("%d\n",total);

}

return 0;

}

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客