主要操作:
合并:unionElements(p,q);
查找:find(p);
class UnionFind{
private:
int* id;
int count;
public:
UnionFind(int n){
count=0;
id=new int[n];
for(int i=0;i<n;i++)
id[i]=i;
}
~UnionFind(){
delete [] id;
}
int find(int p){
assert(p>=0&&p<count);
return id[p];
}
bool isConnected(int p,int q){
return find(p)==find(q);
}
//存在很大优化空间
void unionElements(int p,int q){
int pID=find(p);
int qID=find(q);
if(pID==qID)
return;
for(int i=0;i<count;i++){
if(id[i]==pID)
id[i]=qID;
}
}
};
改进版本:
将每个元素看作是一个节点,parent(i)为它的根节点,进行连接操作时,直接把元素指向根节点即可。
class unionFind{
private:
int* parent;
int count;
public:
UnionFind(int count){
parent=new int[count];
this->count=count;
for(int i=0;i<count;i++)
parent[i]=i;
}
~unionFind(){
delete [] parent;
}
//找p元素的根节点
int find(int p){
assert(p>=0&&p<count);
while(p!=parent[p])
p=parent[p];
return p;
}
//判断连接状态
bool isConnected(int p,int q){
return find(p)==find(q);
}
//连接操作
void unionElements(int p,int q){
int pRoot=find(p);
int qRoot=find(q);
if(pRoot==qRoot)//仍然可以优化
return;
parent[pRoot]=qRoot;
}
};
上面标记的优化之处在于:当连接两个元素时,有可能将大集合中的根节点指向元素少的集合的根节点,这样树的高度就会大幅增加,给查找带来了不便。因此,可以事先比较集合的元素个数,保证每次都能把小集合中的元素指向元素多的集合的根节点。
优化如下:
class unionFind{
private:
int* parent;
//记录集合的大小
int* sz;
int count;
public:
UnionFind(int count){
parent=new int[count];
sz=new int[count];
this->count=count;
for(int i=0;i<count;i++){
parent[i]=i;
sz[i]=1; //初始每个集合大小都为1
}
}
~unionFind(){
delete [] parent;
delete [] sz;
}
int find(int p){
assert(p>=0&&p<count);
while(p!=parent[p])
p=parent[p];
return p;
}
bool isConnected(int p,int q){
return find(p)==find(q);
}
void unionElements(int p,int q){
int pRoot=find(p);
int qRoot=find(q);
if(pRoot==qRoot)
return;
if(sz[pRoot]<sz[qRoot]){
parent[pRoot]=qRoot;
sz[qRoot]+=sz[pRoot];//集合大小更新
}
else{
parent[qRoot]=pRoot;
sz[pRoot]+=sz[qRoot];//集合大小更新
}
}
};