# 并查集详解【转载】

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;                                                                           //方丈只好委委屈屈地当了师太的手下啦

}

（网址丢了）

（这里插入一下学长给我讲的压缩路径的代码，我也讲不出来，大家自行领悟。）

#include<cstdio>
#include<string.h>
int  pre[1050];
int find(int m){
if(pre[m]==m)
return m;

else
return pre[m]=find(pre[m]);     //这个的find部分能实现上图的变换。找到每个城镇的最终根节点。
}
void un(int x,int y)
{
int m=find(x),n=find(y);
if(m!=n)
{
pre[n]=m;
}
}
int main()
{
int N,M,a,b,i,j;
while(scanf("%d",&N)&&N)
{
scanf("%d",&M);
for(i=1;i<=N;i++)
pre[i]=i;
for(i=1;i<=M;i++)
{
scanf("%d%d",&a,&b);
un(a,b);
}
int k=0  ;
for(i=1;i<=N;i++)
{
if(pre[i]==i)
k+=1;
}
printf("%d\n",k-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;

}

10-22 477

03-16 3万+
10-11 95
03-07 129
07-12 101
02-25 764
02-26 372
07-29 7317
12-09 271
08-20
12-18 41万+
02-15