并查集

参考文章:http://blog.csdn.net/dellaserss/article/details/7724401/

定义

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。

举例

杭电1232畅通工程

首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性问题。比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块。像畅通工程这题,问还需要修几条路,实质就是求有几个连通分支。如果是1个连通分支,说明整幅图上的点都连起来了,不用再修路了;如果是2个连通分支,则只要再修1条路,从两个分支中各选一个点,把它们连起来,那么所有的点都是连起来的了;如果是3个连通分支,则只要再修两条路……

以下面这组数据输入数据来说明

4 2 1 3 4 3

第一行告诉你,一共有4个点,2条路。下面两行告诉你,1、3之间有条路,4、3之间有条路。那么整幅图就被分成了1-3-4和2两部分。只要再加一条路,把2和其他任意一个点连起来,畅通工程就实现了,那么这个这组数据的输出结果就是1。好了,现在编程实现这个功能吧,城镇有几百个,路有不知道多少条,而且可能有回路。 这可如何是好?

实现思路

int pre[1000 ];
int
Find(int x) { int r=x; while(r!=pre[r]) r=pre[r]; return r; }
void join(int x,int y)  
{  
    int fx=Find(x),fy=Find(y);  
    if(fx!=fy)  
    {  
        pre[fy]=fx;  
    }  
}   

并查集由一个整数型的数组和两个函数构成。数组pre[]记录了每个点的前导点是什么,函数find是查找,join是合并。

但是这么实现有一个缺点:最后的树状结构会变成什么样,我也完全无法预计,一字长蛇阵也有可能。这样查找的效率就会比较低下。最理想的情况就是所有人的直接上级都是根节点,一共就两级结构,只要找一次就找到根节点了。哪怕不能完全做到,也最好尽量接近。这样就产生了路径压缩算法,查找的时候动态优化树结构。 修改后的查找算法如下:

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;  
}  

杭电1232畅通工程   完整答案

 1     #include<iostream>  
 2     using namespace std;  
 3       
 4     int  pre[1050];  
 5     bool t[1050];               //t 用于标记独立块的根结点  
 6       
 7     int Find(int x)  
 8     {  
 9         int r=x;  
10         while(r!=pre[r])  
11             r=pre[r];  
12           
13         int i=x,j;  
14         while(pre[i]!=r)  
15         {  
16             j=pre[i];  
17             pre[i]=r;  
18             i=j;  
19         }  
20         return r;  
21     }  
22       
23     void mix(int x,int y)  
24     {  
25         int fx=Find(x),fy=Find(y);  
26         if(fx!=fy)  
27         {  
28             pre[fy]=fx;  
29         }  
30     }   
31       
32     int main()  
33     {  
34         int N,M,a,b,i,j,ans;  
35         while(scanf("%d%d",&N,&M)&&N)  
36         {  
37             for(i=1;i<=N;i++)          //初始化   
38                 pre[i]=i;  
39               
40             for(i=1;i<=M;i++)          //吸收并整理数据   
41             {  
42                 scanf("%d%d",&a,&b);  
43                 mix(a,b);  
44             }  
45               
46               
47             memset(t,0,sizeof(t));  
48             for(i=1;i<=N;i++)          //标记根结点  
49             {  
50                 t[Find(i)]=1;  
51             }  
52             for(ans=0,i=1;i<=N;i++)  
53                 if(t[i])  
54                     ans++;  
55                       
56             printf("%d
",ans-1);  
57               
58         }  
59         return 0;  
60     }//dellaserss  

原文思路更加详细与清晰,如有疑惑推荐阅读原文

原文地址:https://www.cnblogs.com/ouym/p/7541943.html