并查集模板
模板1:
这里也可以应用一个简单的启发式策略——按秩合并。该方法使用秩来表示树高度的上界,在合并时,总是将具有较小秩的树根指向具有较大秩的树根。简单的说,就是总是将比较矮的树作为子树,添加到较高的树中。简单的说,就是总是将比较矮的树作为子树,添加到较高的树中。为了保存秩,需要额外使用一个与 parent 同长度的数组,并将所有元素都初始化为 0。
1 const int maxn=505; 2 int parent[maxn],rank_[maxn]; 3 //构造并查集 4 void p(int n) 5 { 6 for(int i=0;i<n;i++) 7 parent[i]=i; 8 for(int i=0;i<n;i++) 9 rank_[i]=0; 10 } 11 //路径压缩 12 int Find(int x) 13 { 14 if(x!=parent[x]) 15 x=Find(parent[x]); 16 return parent[x]; 17 } 18 //并查集合并 19 void union_p(int x,int y) 20 { 21 if((x=Find(x))==(y=Find(y))) 22 return; 23 if(rank_[x]>rank_[y]) 24 parent[y]=x; 25 else 26 { 27 parent[x]=y; 28 if(rank_[x]==rank_[y]) 29 rank_[y]++; 30 } 31 }
模板2:
除了按秩合并,并查集还有一种常见的策略,就是按集合中包含的元素个数(或者说树中的节点数)合并,将包含节点较少的树根,指向包含节点较多的树根。这个策略与按秩合并的策略类似,同样可以提升并查集的运行速度,而且省去了额外的 rank_ 数组。
这样的并查集具有一个略微不同的定义,即若 parent 的值是正数,则表示该元素的父节点(的索引);若是负数,则表示该元素是所在集合的代表(即树根),而且值的相反数即为集合中的元素个数。
如果要获取某个元素 x 所在集合包含的元素个数,可以使用 -parent[find(x)] 得到。
1 const int maxn=505; 2 int parent[maxn],rank_[maxn]; 3 void p(int n) 4 { 5 for(int i=0;i<n;i++) 6 parent[i]=-1; 7 } 8 int Find(int x) 9 { 10 if(parent[x]<0)return x; 11 parent[x]=Find(parent[x]); 12 return parent[x]; 13 } 14 void union_(int x,int y) 15 { 16 if((x=Find(x)==(y=Find(y)))) 17 return; 18 if(parent[x]<parent[y]) 19 { 20 parent[x]+=parent[y]; 21 parent[y]=x; 22 } 23 else 24 { 25 parent[y]+=parent[x]; 26 parent[x]=y; 27 } 28 }