并查集:力扣547. 朋友圈(从算法4中得到的启示,提供多种方法)

1、题目描述:
在这里插入图片描述
在这里插入图片描述
2、题解:
参考算法第4版1.5节 union-find算法。
首先直到几个概念:触点(每个人),连接(两个人之间的关系),连通分量或者分量(朋友圈的个数)
具体见代码的注释:
方法一:quick-find
图片为算法4中的演示
在这里插入图片描述
数组ID为以触点为索引的数组

class Solution:
    def findCircleNum(self, M: List[List[int]]) -> int:
        #并查集
        n = len(M)
        ID = [0] * n 
        #初始化
        for i in range(n):
            ID[i] = i 
        #初始化全局变量:分量的个数
        self.count = n 
        #寻找分量标识符
        def find(m):
            return ID[m]
        #连接分量
        def union(p,q):
            #将p,q归并到相同的分量中
            p_id = find(p)
            q_id = find(q)
            #如果p,q已经在相同的分量中,则忽略
            if p_id == q_id:
                return 
            #将p的分量重命名为q的名称
            for i in range(len(ID)):
                if ID[i] == p_id:
                    ID[i] = q_id
            self.count -= 1
        #进行处理,这时如果没有数据,是输入输出的接口,也可以把上面的函数写在外面
        for i in range(n):
            for j in range(i,n):
                if M[i][j] == 1 : #处理知道关系的连接
                    if find(i) == find(j):#如果已经连通,则忽略
                        continue
                    union(i,j) #归并分量
        return self.count

方法二:quick-union
图片为算法4中的演示
在这里插入图片描述
数组ID为以触点为索引的数组,但是有了新的定义,每个触点所对应的ID[]元素都是同一个分量中的另一个触点的名称(可能是自己)----这种联系称为链接。当且仅当分别由两个触点开始的这个过程到达了同一个根触点时,它们存在于同一个连通分量中。

class Solution:
    def findCircleNum(self, M: List[List[int]]) -> int:
        # 并查集 ,quick-union
        n = len(M)
        ID = [0] * n
        # 初始化
        for i in range(n):
            ID[i] = i
            # 初始化全局变量:分量的个数
        self.count = n

        # 寻找分量标识符
        def find(m):
            # 寻找根节点
            while m != ID[m]:
                m = ID[m]
            return m

        # 连接分量
        def union(p, q):
            # 将p,q归并到相同的分量中
            p_id = find(p)
            q_id = find(q)
            # 如果p,q已经在相同的分量中,则忽略
            if p_id == q_id:
                return
                # 将p的根触点连接到q的根触点上
            ID[p_id] = q_id
            self.count -= 1

        # 进行处理,这时如果没有数据,是输入输出的接口,也可以把上面的函数写在外面
        for i in range(n):
            for j in range(i, n):
                if M[i][j] == 1:  # 处理知道关系的连接
                    if find(i) == find(j):  # 如果已经连通,则忽略
                        continue
                    union(i, j)  # 归并分量
        return self.count

方法3:加权quick-union
在这里插入图片描述与方法2相比,只需要添加一个se[]数组维护树的权值就行,这个权值是分量中的触点个数,也就是树中的节点的个数。

class Solution:
    def findCircleNum(self, M: List[List[int]]) -> int:
        # 并查集 ,加权quick-union
        n = len(M)
        ID = [0] * n
        # 初始化
        for i in range(n):
            ID[i] = i
        # 定义(由触点索引的)各个根节点所对应的分量的大小,也就是权值
        sz = [1] * n
        # 初始化全局变量:分量的个数
        self.count = n

        # 寻找分量标识符
        def find(m):
            # 寻找根节点
            while m != ID[m]:
                m = ID[m]
            return m

        # 连接分量
        def union(p, q):
            # 将p,q归并到相同的分量中
            p_id = find(p)
            q_id = find(q)
            # 如果p,q已经在相同的分量中,则忽略
            if p_id == q_id:
                return
            # 将小树根节点连接到大树的根节点
            if sz[p_id] < sz[q_id]:
                ID[p_id] = q_id
                sz[q_id] += sz[p_id]
            else:
                ID[q_id] = p_id
                sz[p_id] += sz[q_id]
            self.count -= 1

        # 进行处理,这时如果没有数据,是输入输出的接口,也可以把上面的函数写在外面
        for i in range(n):
            for j in range(i, n):
                if M[i][j] == 1:  # 处理知道关系的连接
                    if find(i) == find(j):  # 如果已经连通,则忽略
                        continue
                    union(i, j)  # 归并分量
        return self.count
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值