适合需要反复用到查询某个元素归属于哪个集合的运算。
初始化时每个元素自成为单元素集合,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; }