用父亲结点数组实现并查集
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<algorithm> 4 using namespace std; 5 typedef struct ufset *UFset; 6 struct ufset 7 { 8 int parent[100001]; 9 int root[100001]; 10 }UFS; 11 int s[100001],y[100001],d[10000],add; 12 int UFfind(int e,UFset U) 13 { 14 int i,j=e; 15 while(U->root[j]==0) 16 { 17 j=U->parent[j]; 18 } 19 while(j!=e) 20 { 21 i=U->parent[e]; 22 U->parent[e]=j; 23 e=i; 24 } 25 return j; 26 } 27 int UFunion(int i,int j,UFset U) 28 { 29 U->parent[j]+=U->parent[i]; 30 U->root[i]=0; 31 U->parent[i]=j; 32 return j; 33 } 34 int main() 35 { 36 UFset UF; 37 UF=(UFset)malloc(sizeof(UFS)); 38 int e,n,m,k,x,i,a,b,t1,t2; 39 scanf("%d %d",&n,&m); 40 for(e=1;e<=n+1;e++) 41 { 42 UF->parent[e]=1; 43 UF->root[e]=1; 44 } 45 for(i=0;i<m;i++) 46 { 47 scanf("%d %d",&a,&b); 48 t1=UFfind(a,UF);t2=UFfind(b,UF); 49 if(t1!=t2) 50 { 51 UFunion(t1,t2,UF); 52 } 53 } 54 int t=0; 55 for(i=1;i<n+1;i++) 56 { 57 if(UF->root[i]==1) 58 { 59 s[t]=UF->parent[i]; 60 y[t++]=i; 61 } 62 } 63 sort(s,s+t); 64 add=k=0; 65 for(i=1;;i++) 66 { 67 if(add>=n) 68 break; 69 else 70 { 71 add+=s[t-i]; 72 d[k++]=UFfind(y[t-i],UF); 73 } 74 } 75 printf("%d\n",k); 76 sort(d,d+k); 77 for(i=0;i<k;i++) 78 { 79 if(i<k-1) 80 printf("%d ",d[i]); 81 else 82 printf("%d\n",d[i]); 83 } 84 return 0; 85 }
并查集结构简单。在数据结构作业大招秒杀和战争来了中有巧妙的运用
在下面所描述的改进的并查集结构中增加一个根节点数组root,用来记录树的根结点。
当元素e所在结点不是根节点时,root[e]=0,parent[e]=表示其父节点;当元素e所在结点是根节点
时,root[e]=1,parent[e]的值是树中结点的个数.
1 typedef struct ufset *UFset; 2 struct ufset 3 { 4 int parent[100]; 5 int root[100]; 6 }UFS;
UFfind(e.U)运算从元素e相应的结点走到树根处,找出所在集合的元素
加速并查集运算的另一的办法就是采用路径压缩技术,在执行UFfind时,实际是找到了从一个结点
到树根的一条路径。路径压缩技术就是把这条路上的所有结点都改为树根的儿子,实现路径压缩的最简单方法是
在这条路上走两次,第一次找到树根,第二次将路上所有结点的父节点都改为树根。
int UFfind(int e,UFset U) { int i,j=e; while(U->root[j]==0) { j=U->parent[j]; } while(j!=e) { i=U->parent[e]; U->parent[e]=j; e=i; } return j; }
在最坏情况下,合并可能使n个结点的数退化成一条链,在这种情下,对所有元素各执行
一次UFfind将耗时O(N*N) 。所以,尽管,UFunion只需要O(1)时间,当UFfind可能是总时间
耗费很大,为了克服这个缺点。可以做以下改进,使得每次UFind不超过O(longn)时间。在树
根中保存该树的结点数,每次合并时,总将小树合并到大树上。当一个结点从一棵树移到另
一颗数上时,这个节点到树根的距离就增加1,而这个节点所在的树的大小至少增加一倍。
于是并查集中每一个结点至多被移动O(longn)次,从而每个节点到树根的距离不会超过O(longn)。
所以每次UFfind运算只需要O(longn)时间。
int UFunion(int i,int j,UFset U) { if(U->parent[i]<U->parent[j]) { U->parent[j]+=U->parent[i]; U->root[i]=0; U->parent[i]=j; return j; } else { U->parent[j]+=U->parent[i]; U->root[j]=0; U->parent[j]=i; return i; } }