并查集(路径压缩) 畅通工程 HDU1232

树这种数据结构容易出现极端情况,因为在建树的过程中,树的最终形态严重依赖于输入数据本身的性质,比如数据是否排序,是否随机分布等等。比如在输入数据是有序的情况下,构造的BST会退化成一个链表。(BST可以演变成为红黑树或者AVL树等来克服。)

但当我们仅需要连通与否的信息时,层层查找的效率就会很低,这时可以改变树中的多层关系,将树的结构扁平化。

并查集的工作原理如下:

这样查询白面葫芦娃和仙子狗尾巴花是否连通,就省去很多遍历。

树的扁平化由查询函数find完成:

1 int find(int p){
2     while (p != id[p]){
3         // 将p节点的父节点设置为它的爷爷节点
4         id[p] = id[id[p]];
5         p = id[p];
6     }
7     return p;
8 }

 

HDU1232 畅通工程 题意:

在地图上有若干个城镇(看作点),城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性问题。比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块。还需要修几条路,实质就是求有几个连通分支。

通过代码详细解释:

 1 #include<stdio.h>
 2 int p[1000]; //用于描述没个数的上一级,保存当前坐标的上一级,上司
 3 int find(int x){//用于查找 x 的老大,掌门人
 4     int r=x,t,i,j; 
 5     while(p[r]!=r) //最后r即使x的老大
 6         r=p[r];
 7     t=x;
 8     while(p[t]!=r){ //这是利用已知的老大的坐标,压缩路径,将老大所有的手下的的手下的手下.....全部归到只是老大的下一级,为了方便各个小弟查询老大
 9         i=p[t];
10         p[t]=r;
11         t=i;
12     }
13     return r;//返回老大的坐标
14 }
15 bool myunion(int x,int y){//用于将两个集合合并在一起
16     if(find(x)!=find(y)){//查询两个数的老大,如果不同老大,将x的老大变成y的老大
17         p[find(y)]=p[find(x)];
18         return true; //返回1用于题目计算需要建的路(打架的次数)
19     }
20     return false;
21 }
22 int main(){
23     int n,m,x,y,i;
24     while(~scanf("%d%d",&n,&m)&&n){
25         int s=n-1;//开始若有n个点,则需要修建n-1条路(打n-1次架)
26         for(i=1;i<=n;i++)//初始化,把所有点都初始化成自己是自己的老大,自成一派
27             p[i]=i;
28         for(i=1;i<=m;i++){
29             scanf("%d%d",&x,&y);
30             if(myunion(x,y))s--;//返回1 则表示两个不同小弟是不同的老大,打了一架后,则需要打s-1次架。
31         }
32         /*计算s还可以用,此时每个门派(集合)都归纳好了,开始看看有多少个不同的集合,即看看有多少个不同的老大,根据有多少个老大,这是在开始看看要打多少次架(帮派间的斗争!!)有n个老大就要打n-1次架 (有n个集合就要n-1次修路)可以用一个数组存储根,在查询有多少个根! */
33         printf("%d\n",s);
34     }
35 }


union函数有两种实现方法,其中快速合并的图解:

Quick-Union 算法和Quick-Find只有find和union两个方法有所不同:

 1 int find(int p){ 
 2     // 寻找p节点所在组的根节点,根节点具有性质id[root] = root
 3     while (p != id[p]) p = id[p];
 4     return p;
 5 }
 6 void union(int p, int q){ 
 7     // Give p and q the same root.
 8     int pRoot = find(p);
 9     int qRoot = find(q);
10     if (pRoot == qRoot) 
11         return;
12     id[pRoot] = qRoot;    // 将一颗树(即一个组)变成另外一课树(即一个组)的子树
13     count--;
14 }

 

posted @ 2018-01-16 09:28  proscientist  阅读(234)  评论(0编辑  收藏  举报