并查集小记

来源:互联网 发布:怎么看算法导论这本书 编辑:程序博客网 时间:2024/05/16 11:30

http://acm.hdu.edu.cn/showproblem.php?pid=1232

题目意思很简单,给出m条道路连通的情况,求n个城市连通至少还需要建多少条道路。应该算是赤裸裸的并查集,不过也可以用prim。借此道题目记录一下并查集的应用。

1,定义一个数组set[1...n],其中set[i]表示元素i所在的集合;用编号最小的元素记录所在集合;

   //传进merge()的a,b即是set[x],set[y].

find(int x){return set[x];}merge(int a,int b){int x=min(a,b),y=max(a,b);for(int k=1;k<=n;k++)if(set[k]==y)set[k]=x;}


2, 1中的算法在进行合并操作时必须搜索所有的元素,复杂度较大,所以可以采用树结构。

       定义数组set[1...n],set[i]= i,i表示本集合,并是集合对应的根;set[i]=j,j<>(不等于)i,则j 是i 的父节点。

find(int x){int r=x;while(r!=set[r])r=set[r];return r;}merge(int x,int y){if(x<y)set[y]=x;elseset[x]=y;
}

3,2中的查找在最坏情况下复杂度仍为O(n),为了避免最坏情况的出现,可以将深度小的树合并到深度大的树中。

find(int x){int r=x;while(r!=set[r])r=set[r];return r;}merge(int x,int y){if(height[x]==height[y]){height[x]=height[x]+1;set[y]=x;}else if(height[x]<height[y])set[x]=y;elseset[y]=x;}

4,进一步优化---压缩路径

     每次查找时,如果路径较长,则修改信息,以便下次查找时速度更快。step:1,找到根节点,2,修改查找路径上所有的节点,将它们都指向根节点。

find(int x){int  r=x;while(r!=set[r])//循环结束,找到根节点r=set[r];int tmp=x,a;while(tmp!=r)//本循环修改查找路径中所有节点{a=set[tmp];set[tmp]=r;tmp=a;}return r;}

最后附上1232代码:

#include<stdio.h>const int maxn = 1005;int set[maxn],height[maxn];void creat(int n){int i;for(i=1;i<=n;i++){set[i] = i;height[i] = 1;}}int find(int x){int r = x;while(r!=set[r])r = set[r];//寻找根节点int tmp = x,a;while(tmp!=r){a = set[tmp];set[tmp] = r;tmp = a;}//路径压缩return r;}void merge(int x,int y)//将深度小的树合并到深度大的树中{if(height[x] == height[y]){height[x]+=1;set[y] = x;}else if(height[x]<height[y])set[x] = y;elseset[y] = x;}int main(){int n,m,i,x,y,a,b;while(scanf("%d%d",&n,&m)!=EOF&&n){if(!m){printf("%d\n",n-1);continue;}creat(n);for(i=1;i<=m;i++){scanf("%d%d",&x,&y);a = find(x);b = find(y);merge(a,b);}int num = 0;for(i=1;i<=n;i++)if(set[i] == i)num++;printf("%d\n",num - 1);}return 0; }


 

	
				
		
原创粉丝点击