一,两个重要操作:(1)找出给定元素所属的集合
(2)合并两个集合
二,原理:保持一组不相交的动态集合s={s1,s2,…sk}。每个集合通过一个代表来识别,代表即集合中的某个元素
三,操作
(1)MAKE-SET(X):建立一个新的集合,其唯一成员就是x(因各集合是不相交的,故要求x没有在其他集合中出现过)
(2)UNION(X,Y):将包含x和y的动态集合合并为一个新的集合
(3)FIND-SET(X):返回一个指针,指向包含x的唯一集合代表
四,不相交集合森林
(1)用有根树来表示集合,树中的每个节点都包含集合的一个成员,每个树表示一个集合。
(2)每个成员仅指向其父节点。每个树的根包含了代表,并且是他自己的父节点。
(3)改进方法:
a)按秩合并:使包含较少节点的树的根指向包含较多节点树的根
对每个结点,用秩表示节点高度的一个上界(即根节点到其某一后代叶子节点的最长路径上变得数目)
b)路径压缩: 是查找路径上的每个节点都指向根节点。(简单,有效)不改变节点的秩。
五,伪代码
P[X]表示x的父节点 RANK[x]存放x节点的秩
MAKE-SET(X) //创建一个单元素集合,并初始秩为0
P[X]<-X
RANK[X]<-0
UNION(X,Y) //合并两个集合
LINK(FIND-SET(x),FIND-SET(Y))
LINK(X,Y) //按秩合并
IF RANK[X]>RANK[Y]
THEN P[Y]<-X
ELSE P[X]<-Y
IF RANK[X]=RANK[Y]
THEN RANK[Y]=RANK[Y]+1
FIND-SET(X)//FIND-SET是一种两趟方法:一趟沿查找路径上升直至找到根 另一趟沿查找路径下降,以便更新每个节点,使之指向根·
IF X!=P[X]
THEN P[X]<-FIND-SET(P[X])
RETURN P[X]
六用c++语言实现
int pre[maxn];
int rank[maxn];
int findset(int x) //带路径压缩
{
if(pre[x]==x)
return x;
else
return pre[x]=findset(pre[x]);
}
void link(int x,int y) //按秩的连接算法
{
if(rank[x]>rank[y])
{
pre[y]=x;
}
else{
pre[x]=y;
if(rank[y]==rank[x])
rank[y]=rank[y]+1;
}
}
void makeset(int x)
{
pre[x]=x;
rank[x]=0;
}
void UNION(int x,int y)
{
int xx=findset(x);
int yy=findset(y);
if(xx!=yy)
link(xx,yy);
}