python 并查集 字典实现(连接、检验联通、增加元素)

python实现并查集

由于想刷算法题的时候能够更方便,于是自己实现了并查集,企图做题的时候可以直接复制粘贴套用自己做的轮子

并查集其实本身是森林,不是图,不需要什么广度优先遍历。好像还蛮快的

但是还有代优化,比如压缩路径还没有做。只做了合并路径时候的一点优化。

具体是看知乎上赞最多的学的 https://zhuanlan.zhihu.com/p/93647900

代码

调用方法,使用方法:

import random
# 待插入轮子代码
def main():
    s = MergeSetInt(10)
    for i in range(20):
        a, b = random.randint(0, 9), random.randint(0, 9)
        print("连接", a, b)
        s.union(a, b)
        s.show()

        a, b = random.randint(0, 9), random.randint(0, 9)
        print(a, b, "是否联通:", s.find(a, b))

if __name__ == '__main__':
    main()

轮子代码

import random


class MergeSetInt:
    """
    并查集简单实现
    元素由 [0,n) 连续的序号组成
    有连接和判断连接两个功能
    需要引入随机库
    """

    class Node:
        def __init__(self, val: int):
            """创建一个初始化的 0 ~ val 的并查集 """
            self.val = val
            self.father = self
            self.deep = 1

        def getFather(self):
            """获取这个节点的父节点,同时可以压缩路径"""
            node = self
            while node.father != node:
                node = node.father
            return node

    def __init__(self, n):
        """生成一个内部含序号为 0到n-1的集合"""
        self._list = [self.Node(i) for i in range(n)]

    def union(self, m: int, n: int) -> bool:
        """合并操作"""
        fatherNode1 = self._list[m].getFather()
        fatherNode2 = self._list[n].getFather()
        if fatherNode1 == fatherNode2:
            # 已经在统一个集合里了
            return False
        if fatherNode1.deep < fatherNode2.deep:
            # 1 从了 2
            fatherNode1.father = fatherNode2
            fatherNode2.deep = max(fatherNode1.deep + 1, fatherNode2.deep)  # 更新高度

        elif fatherNode1.deep > fatherNode2.deep:
            # 2 从了 1
            fatherNode2.father = fatherNode1
            fatherNode1.deep = max(fatherNode1.deep, fatherNode2.deep + 1)
        else:
            # 如果两个树一样高,就可以随机选一个树从了另一个树
            if random.random() < 0.5:
                fatherNode1.father = fatherNode2
                fatherNode2.deep = max(fatherNode1.deep + 1, fatherNode2.deep)  # 更新高度
            else:
                fatherNode2.father = fatherNode1
                fatherNode1.deep = max(fatherNode1.deep, fatherNode2.deep + 1)
        return True

    def show(self):
        for node in self._list:
            while node.father is not node:
                print(node.val, end="->")
                node = node.father
            print(node.val)
        print()

    def find(self, m: int, n: int):
        fatherNode1 = self._list[m].getFather()
        fatherNode2 = self._list[n].getFather()
        return fatherNode1 == fatherNode2

普适性更新

更新内容:

  1. 可以添加元素了,一开始创建的是空并查集
  2. 可以获取并查集中最大的集合的大小
class UnionFindSet:
    """
    数字类型并查集
    一开始生成空的并查集
    可以添加元素、连接元素、判断两个元素是否连接
    添加的元素类型是数字类型,元素和元素之间是不重复的
    """

    class Node:
        def __init__(self, val: int):
            self.val = val
            self.father = self
            self.deep = 1
            self.groupCount = 1  # 包括自身,节点的数量

        def getFather(self):
            """获取这个节点的父节点
            待更新:压缩路径"""
            node = self
            while node.father != node:
                node = node.father
            return node

        def __hash__(self):
            return self.val

    def __init__(self):
        """初始化一个空的数字并查集"""
        self.dic = {}

    def addNode(self, val: int):
        """向并查集中添加一个元素,这个元素必须是数字类型"""
        self.dic[val] = self.Node(val)

    def maxGroupNum(self):
        """
        获得数量最大的集合 的大小
        例如:
        { {1} {2} {3} {4} } >>> 1
        { {1,2,3} {4} } >>> 3
        时间复杂度为On,待更新:简化为O1
        """
        res = 1
        for k, v in self.dic.items():
            if v.groupCount > res:
                res = v.groupCount
        return res

    def union(self, m: int, n: int) -> bool:
        """
        合并操作
        将并查集中的两个元素合并
        """
        if m not in self.dic or n not in self.dic:
            return False
        fatherNode1 = self.dic[m].getFather()
        fatherNode2 = self.dic[n].getFather()

        def con1to2():
            # print("12...")
            # 1 从了 2
            fatherNode1.father = fatherNode2
            fatherNode2.deep = max(fatherNode1.deep + 1, fatherNode2.deep)  # 更新高度
            fatherNode2.groupCount += fatherNode1.groupCount

        def con2to1():
            # print("21...")
            # 2 从了 1
            fatherNode2.father = fatherNode1
            fatherNode1.deep = max(fatherNode1.deep, fatherNode2.deep + 1)
            fatherNode1.groupCount += fatherNode2.groupCount

        if fatherNode1 == fatherNode2:
            # 已经在统一个集合里了
            # print("have")
            return False
        if fatherNode1.deep < fatherNode2.deep:
            # print("a")
            con1to2()
            return True
        if fatherNode1.deep > fatherNode2.deep:
            # print("aa")
            con2to1()
            return True
        else:
            # 如果两个树一样高,就可以随机选一个树从了另一个树
            # print("aaa")
            if random.random() < 0.5:
                con1to2()
                return True
            else:
                con2to1()
                return True

    def show(self):
        """展示并查集"""
        for k, node in self.dic.items():
            while node.father is not node:
                print(node.val, end="->")
                node = node.father
            print(node.val)
        print()

    def find(self, m: int, n: int):
        """检验连通"""
        if m not in self.dic or n not in self.dic:
            return False
        fatherNode1 = self.dic[m].getFather()
        fatherNode2 = self.dic[n].getFather()
        return fatherNode1 == fatherNode2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值