Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
layout post
title 用Python实现的用于不相交集合的数据结构
description 用Python实现的用于不相交集合的数据结构
categories
Python
tags
python
redirect_from
/2018/06/18/

用于不相交集合的数据结构

不相交集合数据结构保持一组不相交的动态集合S={S1,S2,...,Sk},每个集合通过一个代表来识别,代表即集合中的某个成员。集合中的每一个元素是由一个对象表示的,设x表示一个对象,希望支持以下操作

  • MAKE-SET(x):其唯一成员(因而其代表)就是x。因为各集合是不相交的,故要求x没有在其他集合出现过

  • UNION(x, y):将包含x和y的动态集合(比如说Sx和Sy)合并为一个新的集合(并集)

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

不相交集合数据结构有多种应用,其中之一是用于确定一个无向图中连通子图的个数

使用按秩结合路径压缩的启发式方法实现不相交集合森林,从渐进意义上来说是最快的方法

class UndirectedGraph:
    '''
    无向图 `G=(V, E)`
    '''
    def __init__(self, vertexs : list = [], edges : list = []):
        '''
        无向图 `G=(V, E)`

        Args
        ===
        `vertexs` : 顶点集合 `list` contains element which contains one element denote a point

        `edges` : 边集合 `list` contains element which contains two elements denote one edge of two points repectively

        Example
        ===
        ```python
        import notintersectset as graph
        >>> g = graph.UndirectedGraph(['a', 'b', 'c', 'd'], [('a', 'b')])
        ```
        '''
        self.vertexs = vertexs
        self.edges = edges
        self.__findcount = 0
        self.__unioncount = 0
        self.__kcount = 0

    def get_connected_components(self):
        '''
        获取无向图中连通子图的集合
        '''
        self.__findcount = 0
        self.__unioncount = 0
        self.__kcount = 0
        set = Set()
        for v in self.vertexs:
            set.make_set(v)
        for e in self.edges:
            u, v = e
            set1 = set.find(u)
            set2 = set.find(v)
            self.__findcount += 2
            if set1 != set2:
                set.union(set1, set2)
                self.__unioncount += 1
        self.__kcount = len(set.sets)
        return set

    def print_last_connected_count(self):
        '''
        获取上一次连接无向图之后调用函数情况
        '''
        print('the k num:{} the find num:{} the union num:{}'. \
            format(self.__kcount, self.__findcount, self.__unioncount))

class Set:
    '''
    不相交集合数据结构
    '''
    def __init__(self):
        '''
        不相交集合数据结构
        '''
        self.sets = []

    def make_set(self, element):
        '''
        用元素`element`建立一个新的集合
        '''
        self.sets.append({element})

    def union(self, set1, set2):
        '''
        将子集合`set1`和`set2`合并
        '''
        if set1 is None or set2 is None:
            return
        self.sets.remove(set1)
        self.sets.remove(set2)
        self.sets.append(set1 | set2)

    def find(self, element):
        '''
        找出包含元素`element`的集合
        '''
        for set in self.sets:
            if element in set:
                return set
        return None
    
    def __str__(self):
        return str(self.sets)

    def printsets(self):
        '''
        打印集合
        '''
        for set in self.sets:
            print(set)

class ListNode:
    def __init__(self, key = None):
        '''
        采用链表表示不相交集合结点
        '''
        self.first = None
        self.next = None
        self.key = key
    
    def __str__(self):
        return str(self.key)

class List:
    def __init__(self):
        '''
        采用链表表示不相交集合
        '''
        self.rep = None
        self.head = None
        self.tail = None
        self.size = 0
    
    def __str__(self):
        return 'List size:{} and rep:{}'.format(self.size, self.rep)

class ListSet(Set):
    '''
    不相交集合的链表表示
    '''
    def __init__(self):
        '''
        不相交集合的链表表示
        '''
        self.sets = []

    def make_set(self, element):
        '''
        用元素`element`建立一个新的集合
        '''
        list = List()
        node = ListNode(element)  
        if list.size == 0:           
            list.head = node
            list.tail = node
            list.rep = node
            node.first = node
            list.size = 1
        else:
            list.tail.next = node
            list.tail = node 
            node.first = list.head
            list.size += 1
        self.sets.append(list)

    def union(self, set1, set2):
        '''
        将子集合`set1`和`set2`合并
        '''
        self.sets.remove(set1)
        self.sets.remove(set2)
        set1.tail.next = set2.rep
        set1.size += set2.size
        set1.tail = set2.tail

        set2.rep = set1.rep

        node = set2.head
        for i in range(set2.size):
            node.first = set1.rep
            node = node.next

        self.sets.append(set1)

    def unionelement(self, element1, element2):
        '''
        将`element1`代表的集合和`element2`代表的集合合并
        '''
        set1 = self.find(element1)
        set2 = self.find(element2)
        if set1 is None or set2 is None:
            return
        if set1.size < set2.size:
            self.union(set2, set1)
        else:
            self.union(set1, set2)

    def find(self, element):
        '''
        找出包含元素`element`的集合
        '''
        for set in self.sets:
            node = set.rep
            while node != set.tail:
                if node.key == element:
                    return set
                node = node.next
            else:
                if set.tail.key == element: 
                    return set
        return None

class RootTreeNode:
    '''
    有根树结点
    '''
    def __init__(self, key = None, parent = None, rank = None):
        '''
        有根树结点

        Args
        ===
        `key` : 关键字值

        `parent` : 结点的父结点

        `rank` : 结点的秩
        '''
        self.key = key
        self.parent = parent
        self.rank = rank
    
    def __str__(self):
        return 'key:{} rank:{}'.format(self.key, self.rank)

class RootTree:
    '''
    有根树
    '''
    def __init__(self, root = None):
        '''
        有根树
        Args
        ===
        `root` : 有根树的根结点
        '''
        self.root = root
    
    def __str__(self):
        return 'roots:' + str(self.root)

class ForestSet(Set):
    '''
    不相交集合森林
    '''
    def __init__(self):
        self.sets = []

    def make_set(self, element):
        '''
        用元素`element`建立一个新的集合
        '''
        treenode = RootTreeNode(element)
        self.make_set_node(treenode)
        
    def make_set_node(self, node : RootTreeNode):
        '''
        用有根树结点`node`建立一个新的集合
        '''
        tree = RootTree()  
        node.parent = node
        node.rank = 0
        tree.root = node
        self.sets.append(tree)

    @classmethod
    def link(self, node1 : RootTreeNode, node2 : RootTreeNode):
        '''
        连接两个有根树的结点`node1`和`node2`
        '''
        if node1.rank > node2.rank:
            node2.parent = node1
        else:
            node1.parent = node2
            if node1.rank == node2.rank:
                node2.rank += 1
    
    def union(self, x : RootTreeNode, y : RootTreeNode):
        '''
        将有根树结点`x`代表的集合和有根树结点`y`代表的集合合并
        '''
        self.link(self.findnode(x), self.findnode(y))

    def findnode(self, x : RootTreeNode):
        '''
        带路径压缩的寻找集合
        '''
        if x != x.parent:
            x.parent = self.findnode(x.parent)
        return x.parent

    def findnode_nonrecursive(self, x : RootTreeNode):
        '''
        带路径压缩的寻找集合(非递归版本)
        '''
        y = x
        while y != y.parent:
            y = y.parent
        while x != x.parent:
            x.parent = y
            x = x.parent

def connected_components(g: UndirectedGraph):
    '''
    求一个无向图中连通子图的个数

    Args
    ===
    `g` : UndirectedGraph 无向图

    '''
    set = Set()
    for v in g.vertexs:
        set.make_set(v)
    for e in g.edges:
        u, v = e
        set1 = set.find(u)
        set2 = set.find(v)
        if set1 != set2:
            set.union(set1, set2)
    return set

def test_graph_connected():
    '''
    测试无向图链接连通子图
    '''
    g = UndirectedGraph()
    g.vertexs = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
    g.edges.append(('b', 'd'))
    g.edges.append(('e', 'g'))
    g.edges.append(('a', 'c'))
    g.edges.append(('h', 'i'))
    g.edges.append(('a', 'b'))
    g.edges.append(('e', 'f'))
    g.edges.append(('b', 'c'))
    print(g.get_connected_components())
    g.print_last_connected_count()

def test_list_set():
    '''
    不相交集合的链表表示
    '''
    NUM = 16
    set = ListSet()
    for i in range(NUM):
        set.make_set(i)
    for i in range(0, NUM - 1, 2):
        set.unionelement(i, i + 1)
    for i in range(0, NUM - 3, 4):
        set.unionelement(i, i + 2)
    set.printsets()
    set.unionelement(1, 5)
    set.unionelement(11, 13)
    set.unionelement(1, 10)
    set.printsets()
    print(set.find(2))
    print(set.find(9))

def test_forest_set():
    '''
    测试不相交集合森林
    '''
    NUM = 16
    set = ForestSet()
    nodes = []
    for i in range(NUM):
        nodes.append(RootTreeNode(i))
    for i in range(NUM):
        set.make_set_node(nodes[i])
    set.printsets()
    for i in range(0, NUM - 1, 2):
        set.union(nodes[i], nodes[i + 1])
    set.printsets()
    for i in range(0, NUM - 3, 4):
        set.union(nodes[i], nodes[i + 2])
    set.printsets()

if __name__ == '__main__':
    test_graph_connected()
    test_list_set()
    test_forest_set()
else:
    pass    

Github Code