不相交集合数据结构保持一组不相交的动态集合S={S1,S2,...,SK},每个集合通过一个代表来识别,代表即集合中的某个成员。

如果x表示一个对象,不相交集合支持以下操作:

MAKE-SET(x):建立一个新的集合,其唯一成员为x。因为各集合是不想交的,故x没有在其它集合中出现。

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

FIND-SET(x):返回包含x的集合。

1.不相交集合的数组表示

在一个数组中保存每个元素所在集合的名称。这样Find操作就是简单的O(1)查找。要执行Union(x,y)操作,假设x在等价类i中,y在等价类j中,

扫描整个数组,将所有的i变为j。连续N-1次Union操作就要花费Θ(N2)的时间。如果Union操作很多,这个界是不可接受的。

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

每一个集合用用一个链表来表示。链表的第一个对象作为它所在集合的代表。链表中每个对象都包含一个集合成员,一个指向下一个对象的指针,

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

Union的简单实现:将x所在的链表拼接到y所在链表的表尾。对于原先x所在链表中的每一个对象都要更新其指向代表的指针。

平均看来,每个操作需要Θ(N)的时间。

加权合并:每个表含表的长度,总是将更短的表连接到长的表的后面。这样,m和MAKE-SET,UNION和FIND-SET操作要花费(m+nlgn)的时间。

class SetNode(object):
def __init__(self,key):
self.key=key
self.next=None
self.rep=None
class SetEntry(object):
def __init__(self):
self.head=None
self.tail=None
self.len=0
class DisjSet(object):
def __init__(self,node):
self.setlist=[]
def make_set(self,node):
S=SetEntry()
S.head=node
S.tail=node
S.len=1
node.rep=node
self.setlist.append(S)
def find(self,node):
return node.rep
def union(self,node_x,node_y):
rep_x=node_x.rep
rep_y=node_y.rep
if rep_x!=rep_y:
for s in self.setlist:
if s.head==rep_x:
set_x=s
elif s.head==rep_y:
set_y=s
if set_x.len>=set_y.len:
set_x.tail.next=rep_y
node=rep_y
while node is not None:
node.rep=rep_x
node=node.next
set_x.tail=set_y.tail
set_x.len=set_x.len+set_y.len
self.setlist.remove(set_y)
return rep_x
else:
set_y.tail.next=rep_x
node=rep_x
while node is not None:
node.rep=rep_y
node=node.next
set_y.tail=set_x.tail
set_y.len+=set_x.len
self.setlist.remove(set_x)
return rep_y

 3.不相交集合森林

使用树来表示一个集合,树的根用来作为集合的代表。树的每个节点都含有元素的数据以及一个指向父节点的指针。根节点的指针为空。

可以用数组来非显式的来表示树:数组的每个成员T[i]表示元素i的父节点,如果i是根,取p[i]为0或者-1。

如果任意执行Union操作,树可能会变为退化树,有几种方法可以避免这种情况

3.1 灵巧求并算法

总是让更小的树成为较大的树的子树,称为按大小求并。另一种方法是按高度求并。

这样的话,任何节点的深度都不会超过logN,Find操作的运行时间是O(logN),而连续M次操作则花费O(MlogN)。

实现时,让数组每个元素包含它的树的大小的负值。

class DisjSet(object):
def __init__(self,size):
self.list=[-1]*size
def find(self,x):
if self.list[x]<0:
return x
else:
return self.find(self.list[x])
def union(self,x,y):
set_x=self.find(x)
set_y=self.find(y)
if set_x!=set_y:
if self.list[set_x]>self.list[set_y]:
self.list[set_y]+=self.list[set_x]
self.list[set_x]=set_y
return set_y
else:
self.list[set_x]+=self.list[set_y]
self.list[set_y]=set_x
return set_x

3.2 路径压缩

路径压缩在一次Find(X)操作期间执行,从X到根的路径上的每一个节点都使它的父节点变成根。

路径压缩与按大小求并是完全兼容的,而不完全与按高度求并兼容。路径压缩时每棵树的高度会发生变化,可以对每棵树所存储的高度估计,用秩rank表示。

class DisjSet_with_rank(object):
def __init__(self,size):
self.list=[-1]*size
def find(self,x):
if self.list[x]<0:
return x
else:
self.list[x]=self.find(self.list[x])
return self.list[x]
def union(self,x,y):
set_x=self.find(x)
set_y=self.find(y)
if set_x!=set_y:
if self.list[set_x]<self.list[set_y]:
self.list[set_y]=set_x
else:
if self.list[set_x]==self.list[set_y]:
self.list[set_y]-=1
self.list[set_x]=set_y

路径压缩的显式表示  

class SetNode(object):
def __init__(self,key):
self.parent=None
self.key=key
self.rank=1
def find(node):
if node.parent is None:
return node
else:
node.parent=find(node.parent)
return node.parent
def union(x,y):
x=find(x)
y=find(y)
if x!=y:
if x.rank<=y.rank:
if x.rank==y.rank:
y.rank+=1
x.parent=y
return y
else:
y.parent=x
return x

  

不相交集合ADT的更多相关文章

  1. 不相交集合ADT -数据结构(C语言实现)

    读数据结构与算法分析 不相交集合 等价关系 满足三个性质 - 自反性 - 对称性 - 传递性 基本数据结构 基本思路 使用一个数组,下标表示该集合,内容表示指向的父亲 实现 类型声明 typedef ...

  2. 算法实践--不相交集合(Disjoint Sets)

    什么是不相交集合(Disjoint Sets) 是这样的一组set,任何元素最多只能在一个set中 至少支持查找Find和合并Union操作 实现方式(基于树) 每个set都是一棵树 每棵树都由树的根 ...

  3. 【并查集】 不相交集合 - 并查集 教程(文章作者:Slyar)

    最近写了一个多星期的并查集,一瞬间贴出这么多解题报告,我想关于并查集的应用先告一段落吧,先总结一下. 在网上看到一篇关于并查集比较好的教程(姑且允许我这么说吧),不转过来是在可惜.献给爱学习的你 文章 ...

  4. 并查集(不相交集合)详解与java实现

    目录 认识并查集 并查集解析 基本思想 如何查看a,b是否在一个集合? a,b合并,究竟是a的祖先合并在b的祖先上,还是b的祖先合并在a上? 其他路径压缩? 代码实现 结语 @(文章目录) 认识并查集 ...

  5. python学习笔记(集合的使用)

    集合 集合(set):把不同的元素组成一起形成集合,是python基本的数据类型. 集合元素(set elements):组成集合的成员 为什么需要集合? 集合的作用 1 .列表去重复数据 按照现有知 ...

  6. [学习笔记&教程] 信号, 集合, 多项式, 以及各种卷积性变换 (FFT,NTT,FWT,FMT)

    目录 信号, 集合, 多项式, 以及卷积性变换 卷积 卷积性变换 傅里叶变换与信号 引入: 信号分析 变换的基础: 复数 傅里叶变换 离散傅里叶变换 FFT 与多项式 \(n\) 次单位复根 消去引理 ...

  7. Java 知识笔记 - 类、集合、多线程、IO、JVM(最后一次更新,2019年02月17日)

    目录 Class 内部类.静态内部类.匿名内部类.局部内部类 Collection Java Collection Set Queue Map Collections Arrays System Co ...

  8. 图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

    图的连通性问题:无向图的连通分量和生成树,所有顶点均由边连接在一起,但不存在回路的图. 设图 G=(V, E) 是个连通图,当从图任一顶点出发遍历图G 时,将边集 E(G) 分成两个集合 T(G) 和 ...

  9. Union-Find 检测无向图有无环路算法

    不相交集合数据结构(Disjoint-set data structure)是一种用于跟踪集合被分割成多个不相交的子集合的数据结构,每个集合通过一个代表来标识,代表即集合中的某个成员. Union-F ...

随机推荐

  1. hellogcc -100GDB技巧

    https://github.com/hellogcc/100-gdb-tips/blob/master/README.md

  2. 运维知识体系v0.5

    http://www.90qj.com/?post=318http://ixdba.blog.51cto.com/2895551/1751377   运维知识体系v0.5-(运维社区-赵班长出品,欢迎 ...

  3. 萌货猫头鹰登录界面动画iOS实现分析

    动画效果仿自国外网站readme.io的登录界面,超萌可爱的猫头鹰,具体效果如下图所示. 动画实现核心: 动画核心的是用到了iOS中UIView的transform属性,然后根据尺寸坐标对四张图片进行 ...

  4. SerialPort基本小例

    SerialPort是用于串口通信的控件与VB6中的MSCOMM控件相似,使用很方便... vb.net CodeImports System.IO.PortsImports System.TextP ...

  5. cell函数总结

    元胞数组: 元胞数组是MATLAB的一种特殊数据类型,可以将元胞数组看做一种无所不包的通用矩阵,或者叫做广义矩阵.组成元胞数组的元素可以是任何一种数据类型的常数或者常量,每一个元素也可以具有不同的尺寸 ...

  6. npm更换淘宝镜像

    镜像使用方法(三种办法任意一种都能解决问题,建议使用第三种,将配置写死,下次用的时候配置还在): 1.通过config命令 npm config set registry https://regist ...

  7. MongoDB基本操作

    转:http://zhidao.baidu.com/link?url=D5s4tNnP6hH0XPZkFooV-o4MQH3pNZh7C3rtLX_HtVWaIyBRhLIUyoZYfVv15l2eS ...

  8. jQuery UI - draggable 中文API

    ·概述 在任何DOM元素启用拖动功能.通过单击鼠标并拖动对象在窗口内的任何地方移动. 官方示例地址:http://jqueryui.com/demos/draggable/ 所有的事件回调函数都有两个 ...

  9. hasLayout与Block formatting contexts的学习(下)

    BFC布局规则: 内部的Box会在垂直方向,一个接一个地放置. Box垂直方向的距离由margin决定.属于同一个BFC的两个相邻Box的margin会发生重叠 每个元素的margin box的左边, ...

  10. css扁平化博客学习总结(三)header代码实现

    页头.banner.正文.页脚的宏观布局 1.布局顺序的重要性: 由大到小,着眼最大的部分,慢慢细分. <body> <header><!-- 页头开始 --> & ...