并查集算法应用

并查集概念

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。

并查集也被称为不相交集数据结构。顾名思义,并查集主要操作是合并与查询,它是把初始不相交的集合经过多次合并操作后合并为一个大集合,然后可以通过查询判断两个元素是否已经在同一个集合中了。

在Python中,并查集可以通过list或者dict等数据结构实现

547. 朋友圈

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

并查集的主要方法有:查找/连通/判断是否连通

这道题的代码相对较为简单:

class Solution:
    def findCircleNum(self, M: List[List[int]]) -> int:
        n = len(M)
        p = [[i] for i in range(n)] #先生成并查集的节点
        ans = n
        for i in range(n):
            for j in range(i):
                if M[i][j] == 1 and p[i] is not p[j]: #如果i和j是好朋友,但p中没有显示则
                    p[i] += p[j]  #在p[i]中加入p[j]
                    for k in p[j]: #对于p[j]中的每个数字,定义其根节点为p[i]
                        p[k] = p[i]
                    ans -= 1 #朋友圈的数量 - 1
        print(p) #本题结果为[[1, 0], [1, 0], [2]]
        return ans

在这道题目中传统的方式可以将各个方法都实现出来,如下:

class Solution(object):
    def findCircleNum(self, M):
        """
        :type M: List[List[int]]
        :rtype: int
        """
        # 声明表示集合的树,用数组表示树,如 parent[5] == -1 表示
        # 5 是某个集合的代表,同时也是树根
        parent = [-1] * len(M)
        # 为各个集合的深度排个序
        rank = dict()
        # 只遍历左下部分
        for i in range(len(M)):
            for j in range(i):
                if M[i][j] == 1:
                    # 如果两人是朋友,就把两人放入同一个集合
                    self.union(i, j, parent, rank)
        ans = 0
        print(parent)
        for i in parent: # 查看有多少集合,即多少个朋友圈
            if i == -1:
                ans += 1
        return ans
    
    def findRoot(self, num, parent):
        """
        :num:某个小朋友
        :parent:多个集合树
        一直找到这个小朋友所在集合的代表,即树根
        """
        while parent[num] != -1:
            num = parent[num]
        return num

    def union(self, x, y, parent, rank):
        """
        x,y 表示满足朋友关系的两小朋友
        """
        if x == y: # 表示自己和自己是朋友,这个直接返回
            return
        x = self.findRoot(x, parent) # 找到所在集合的代表,可能就是自己
        print("x is",x)
        y = self.findRoot(y, parent)
        print("y is",y)
        rank_x = rank[x] if x in rank else 0 # 查询该集合目前的深度
        rank_y = rank[y] if y in rank else 0
        if x == y: # 检测到 x, y 已经是在同一个集合了,直接返回
            return
        if rank_x > rank_y: # 这里是做路径压缩的,避免树太深使得 findRoot 函数耗时过多
            parent[y] = x
        elif rank_x < rank_y:
            parent[x] = y
        else:
            parent[x] = y
            rank[y] = 1

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值