并查集

1 并查集实际上可以看做是一个有向图的树,除了根节点指向自己,其它的节点都是向上指,指向其父节点,

class UnionFindSets:
    def __init__(self, M):
        self.tree_num = len(M)
        # 初始化树的节点数
        self.tree_node_num = [1] * self.tree_num
        # 索引是节点,数组中的值是其父节点
        self.parent = [i for i in range(self.tree_num)]
    def union(self, node_i, node_j):
        root_i = self.find(node_i)
        root_j = self.find(node_j)
        # 如果是同一个根节点,则返回,无需合并,否则合并
        if root_i == root_j:
            return
        # 合并的时候小树往大树上合并,这样更加均衡,因为大树一般较高,往小树合并的话高度又会加一,导致越合越高,
        # 并且计算树的节点个数,方便合并时比较树的大小,
        if self.tree_node_num[root_i] > self.tree_node_num[root_j]:
            self.parent[root_j] = root_i
            self.tree_node_num[root_i] += self.tree_node_num[root_j]
        else:
            self.parent[root_i] = root_j
            self.tree_node_num[root_j] += self.tree_node_num[root_i]
        # 每合并一次减少一个树,树的数量减一
        self.tree_num -= 1
    # 查找节点所在树的根
    def find(self, node):
        # 如果还没有到根节点,就继续往上找
        while node != self.parent[node]:
            # 为了压缩树的高度,将当前节点连到其爷爷节点上,这样树的高度不超过3
            self.parent[node] = self.parent[self.parent[node]]
            # 更新当前节点,即向上走一个到其父节点
            node = self.parent[node]
        # 返回树的根节点
        return node
from typing import List
class Solution:
    def findCircleNum(self, M: List[List[int]]) -> int:
        uf = UnionFindSets(M)
        l = len(M)
        for row in range(l):
            for col in range(row):
                # 如果为1,说明是朋友,进行连接
                if M[row][col]:
                    uf.union(row, col)
        return uf.tree_num
View Code

2 并查集的优化有两种策略:

(1)路径压缩;

有“隔代压缩”与“完全压缩”。

       “隔代压缩”性能比较高,虽然压缩不完全,不过多次执行“隔代压缩”也能达到“完全压缩”的效果,我本人比较偏向使用“隔代压缩”的写法。
       “完全压缩”需要借助系统栈,使用递归的写法。或者先找到当前结点的根结点,然后把沿途上所有的结点都指向根结点,得遍历两次。

(2)按秩合并。

       秩也有两种含义:① 秩表示以当前结点为根结点的子树结点总数,即这里的“秩”表示 size 含义;② 秩表示以当前结点为根结点的子树的高度,即这里的“秩”表示 rank 含义

posted on 2020-04-28 17:59  吃我一枪  阅读(175)  评论(0编辑  收藏  举报

导航