不相交集合ADT

不相交集合数据结构保持一组不相交的动态集合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

时间: 2024-10-12 13:16:56

不相交集合ADT的相关文章

[算法系列之二十八]并查集(不相交集合)

一 概述 并查集(Disjoint set或者Union-find set)是一种树型的数据结构,常用于处理一些不相交集合(Disjoint Sets)的合并及查询问题. 有一个联合-查找算法(union-find algorithm)定义了两个操作用于此数据结构: Find:确定元素属于哪一个子集.它可以被用来确定两个元素是否属于同一子集. Union:将两个子集合并成同一个集合. 因为它支持这两种操作,一个不相交集也常被称为联合-查找数据结构(union-find data structur

计算机算法学习(1) - 不相交集合数据结构

不相交集合 故名思意就是一种含有多个不相交集合的数据结构.典型的应用是确定无向图中连通子图的个数.其基本操作包括: Make-Set(x):建立一个新的集合,集合的成员是x: Union(x,y): 将包含x和y的集合合并为一个集合: Find-Set(x): 返回指向包含x的集合的指针: 下面是一个例子,(a)是一个无向图,(b)是使用不相交集合来找连通子图的个数.做法是初始为各个顶点为一个集合,然后遍历各个边,把边的端点的集合进行合并,当处理完所有的边,能连通的顶点就在一个集合里了,这样就生

算法导论 第二十一章:不相交集合森林

在不相交集合中的另一种更快的实现中,用有根树来表示集合.树中的每个成员指向其父节点,每棵树的根包含了代表(representative),并且是他自己的父节点.不相交森林即由多棵这样的树组成,如下图所示: [注:(b)是(a)UNION(e,g)的结果] 采用上述表示方法直观上并不比采用链表表示算法更快,但是可以通过"按秩合并"和"路径压缩"来提升效率. 按秩合并(union by rank): 对于每个节点,用秩表示节点高度的一个上界.在按秩合并中,具有较小秩的跟

不相交集合的数据结构

不相交集合的数据结构 本来想着来实现基于贪婪思想的Kruskal算法-–最小生成树的算法之一. 却发现当我们合并集合时里面还涉及到一个判断"环"的问题,继而有了本篇博文:不相交集合的数据结构. 关于不相交集合的数据结构,这里作一个简单的介绍,更多的可看这里 第一:我们假设我们有n个不相交的集合{Si},i=1~n:其中每个集合中都有一个"代表元素"(这个"代表元素"你可以理解为我们班级中的"班长",对外"班长&quo

并查集:不相交集合

并查集是一种树型的数据结构,其保持着用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.支持三种操作: Make-Set : 用于建立单元素集合. Find-Set:确定元素属于哪一个子集.它可以被用来确定两个元素是否属于同一子集. Union:将两个子集合并成同一个集合. 1.并查集的数组表示 //x表示元素,s[x]表示x所属集合 int s[N]; Make-Set(x){ s[x] = x; } Find-Set(x){ return s[x]; } //将y在所属集合

算法导论 第二十一章:不相交集合的数据结构

不相交集合(Disjoint-set )数据结构保持一组不相交的动态集合S={S(1),S(2),...,S(k)}.每个集合通过一个代表(representative)来识别,即集合中的某个成员.设x表示一个对象,不相交集合支持操作: MAKE-SET(x):建立一个新的结合,其唯一成员(也即代表)就是x.因为各集合是不相交的,故要求x没有在其他集合中出现过. UNION(x,y):将包含x和y的动态集合合并成一个新的集合(即这两个集合的并集).假定在这个操作之前两个集合是不相交的,操作之后,

算法导论——用于不相交集合的数据结构

不相交集合的操作 一些应用涉及将n个不同元素分成一组不相交的集合,常进行两种操作:寻找包含制定元素的唯一集合以及合并两个集合.操作进行以下定于: MAKE-SET(x)建立一个新的集合,仅含有x UNION(x,y)将包含x和y的两个集合合并成一个新的集合,删除原本的集合 FIND-SET(x)返回一个指向包含唯一x的集合的指针 无向图的连通分量就是一个例子. 对于如图所示的4个连通分量,先对每一个单独的点建立一个单独的集合,然后依次根据每条边合并对应的集合,最后形成4个不相交的集合 1 CON

python学习笔记(集合的使用、文件操作、字符编码与转码、函数)

集合 集合(set):把不同的元素组成一起形成集合,是python基本的数据类型. 集合元素(set elements):组成集合的成员 为什么需要集合? 集合的作用 1 .列表去重复数据 按照现有知识的解决思路:先设置空列表,然后使用for寻获,把需要去重的列表的第一个数据放到新列表中,然后依次取出第二个数据,把第二个数据和第一个数据作比较,如果不一样,则存入新列表中:以此类推,每取一次都要和新列表中的数据作对比,不一样的则添加入新列表中. 2. 关系测试 比如有学员同时报了python班和l

哈希表 - 集合(set) 和 字典(dict)

集合 简介set :set对象是由hashable 对象所组成的无序集合,set对象的每一个元素要求可进行哈希运算,set 会对内部元素进行去重,每个元素在同一个set 中只会出现一次,由于set对象可变性,所以set 对象自身不可哈希.frozenset : frozenset 对象可以看成一个不可变set对象,是一个可哈希对象,可以最为frozenset.set 的元素或 dict 的 key .创建set对象可哈希对象:在python常见的数据类型中,数值类型,字符串,元组,frozens