适合需要反复用到查询某个元素归属于哪个集合的运算。
初始化时每个元素自成为单元素集合,parent均为-1。此后parent[x]>0时表示元素x的父在数组中的下标;parent[x]<0时表示以元素x为根的集合的元素个数
Find操作可以查询元素归属哪个集合,搜索并返回包含元素x的根即可,有两种方法:
(1)非递归方法:while(parent[x]>=0) x=parent[x];
(2)递归方法:x为根时直接返回x,否则继续Find(parent[x])递归找x的父的根
Union操作进行集合合并,默认把元素Root2嫁接到Root1上:parent[Root1]+=parent[Root2]; parent[Root2]=Root1;
执行一次Union操作需要O(1),而n-1次Union操作可以在O(n)内完成。
性能不好,可能会产生退化了的树。此时Find操作需要从被搜索元素出发,沿父指针链走到根,完成Find(i)的时间复杂度为O(i),而完成n次搜索需要的总时间为O(n^2)
可以使用加权规则得到改进Union操作,使用结点个数探查法求两个UFSets类集合的并,结点个数少的集合嫁接到结点个数多的集合上。执行一次WeightedUnion操作的时间比执行一次Union的时间稍多,但仍在常量界限O(1)范围内。
折叠规则压缩路径的算法CollapsingFind:如果元素j是从元素i到根的路径上的一个结点,并且元素j的父结点不是根,则把元素j的父结点置为根
const int DefaultSize=10;
class UFSets{
public:
UFSets(int sz=DefaultSize);
~UfSets(){delete []parrent;}
UFSets& operator=(UFSets& R);
void Union(int Root1,int Root2);
int Find(int x);
void WeightedUnion(int Root1,int Root2);
int CollapsingFind(int i);
private:
int *parent;
int size;
}
UFSets::UFSets(int sz){
size=sz;
parent=new int[size];
for(int i=0;i<size;i++) parent[i]=-1; //初始化时每个元素自成为单元素集合
}
int UFSets::Find(int x){
//非递归方法,搜索并返回包含元素x的根
while(parent[x]>=0) x=parent[x];
return x;
// 一种递归方法,x为根时直接返回x,否则递归找x的父的根
// if(parent[x]<0) return x;
// else return Find(parent[x]);
}
void UFSets::Union(int Root1,int Root2){
parent[Root1]+=parent[Root2];
parent[Root2]=Root1;
}
void UFSets::WeightedUnion(int Root1,int Root2){
int r1=Find(Root1),r2=Find(Root2),temp;
if(r1!=r2){
temp=parent[r1]+parent[r2];
if(parent[r2]<parent[r1]){ //r2结点个数更多
parent[r1]=r2;
parent[r2]=temp;
}
else{
parent[r2]=r1;
parent[r1]=temp;
}
}
}
int UFSets::CollapsingFind(int i){
for(int j=i;parent[j]>=0;j=parent[j]); //在包含i的树中搜索根j
while(i!=j){ //将从j到根的路径上所有结点都变成根的子女
int temp=parent[i];
parent[i]=j;
i=temp;
}
return j;
}