您的位置:首页 > 理论基础 > 数据结构算法

算法导论学习笔记(15)——用于不相交集合的数据结构

2012-05-04 14:51 651 查看
用于不相交集合的数据结构

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

1. 不相交集合上的操作

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

一些操作:

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

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

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所在链表中的每一个对象,都需要更新其指向代表的指针。

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

使用加权合并策略,对m个MAKE-SET, UNION和FIND-SET操作所构成的序列(其中n个MAKE-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采用了两趟方法:一趟沿查找路径上升,直至找到根;第二趟沿查找路径下降,以便更新每个结点,使之直接指向根。

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

附录:

[cpp]
view plaincopyprint?

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));
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: