算法导论学习笔记-第二十一章-用于不相交集合的数据结构

第二十一章 用于不相交集合的数据结构

 

总结:这一章讲了并查集的相关概念,以及主要的MAKE-SET, UNION, FIND-SET操作,并给出了并查集的链表表示和森林表示方式。

 

1.    不相交集合上的操作

不相交集合数据结构保持一组不相交的动态集合,每个集合通过一个代表来标识,代表即集合中的某个成员。

 

一些操作:

MAKE-SET(x): 建立一个新的集合,其唯一成员为x

UNION(x,y): 将包含xy的动态集合合并为一个新的集合。

FIND-SET(x): 返回一个指针,指向包含x的集合的代表。

 

应用:例如,确定一个无向图中连通子图的个数。

 

2.    不相交集合的链表表示

每一个集合用一个链表表示。每个链表中的第一个对象作为它所在集合的代表。

每一个对象的结构:

1)集合成员

2)指向包含下一个集合成员的对象的指针

3)指向代表的指针

每个链表都包含head指针和tail指针,head指向链表的代表,tail指向链表中最后的对象。

 

MAKE-SET(x): O(1),创建新链表,其仅有对象为x

FIND-SET(x): O(1),返回x指向代表的指针

UNION(x,y): x所在的链表拼接到y所在链表的表尾。注意,对于原先x所在链表中的每一个对象,都需要更新其指向代表的指针。

 

加权合并启发式策略:设每个表还包括了表的长度,合并时,总是把较短的表拼到较长的表上。

使用加权合并策略,对mMAKE-SET, UNIONFIND-SET操作所构成的序列(其中nMAKE-SET操作,因此UNION操作的次数至多为n-1),花费的总时间为O(m+nlgn)

 

3.    不相交集合森林

利用有根树来表示集合,每棵树表示一个集合。树中的每个成员仅指向其父节点,树的根的父节点仍是自己,且树的根即集合的代表。

 

启发式策略:

1)按秩合并

合并时,使包含较少结点的树的根指向包含较多结点的树的根。秩,结点高度的上界。因此,即,具有较小秩的根在UNION操作中要指向具有较大秩的根。

 

2)路径压缩

FIND-SET操作中,使查找路径上的每个结点都直接指向根节点。

 

rank[x]表示结点的秩,即x的高度的上界,p[x]表示x的父节点

 

伪代码

MAKE-SET(x)

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)

if x!=p[x]

      then p[x] <- FIND-SET(p[x])

return p[x]

 

FIND-SET采用了两趟方法:一趟沿查找路径上升,直至找到根;第二趟沿查找路径下降,以便更新每个结点,使之直接指向根。

 

分析:当同时采用按秩合并和路径压缩时,对mMAKE-SET, UNION, FIND-SET的操作序列,总的运行时间可看作与m成线性关系。

 

附录:

typedef struct _node { _node* parent; int rank; }node; node *s[5000]; void makeSet(int x) { s[x]=new node; s[x]->rank=0; s[x]->parent=s[x]; } node* findSet(node* s) { if(s!=s->parent) { s->parent=findSet(s->parent); } return s->parent; } void link(node *s1, node *s2) { if(s1==s2) return; if(s1->rank > s2->rank) s2->parent=s1; else { s1->parent=s2; if(s1->rank==s2->rank) s2->rank++; } } void _union(node *s1, node *s2) { link(findSet(s1),findSet(s2)); }

你可能感兴趣的:(算法学习)