python 通过字典实现的并查集按秩合并(并查集merge中size的优化) 解释及模板

并查集有两种在 merge() 中的优化

一种基于 size 的;
一种基于 rank 的,rank的优化更彻底;
并查集还有在 find() 中的优化,就是路径压缩
所有操作均基于 并查集路径压缩及实现的模板实现

基于size的优化:

这里先看一个简单的例子来理解优化过程
python 通过字典实现的并查集按秩合并(并查集merge中size的优化) 解释及模板_第1张图片

index 1 2 3 4
father None 1 1 None

首先进行合并操作

将1节点与4节点连通
相当于代码模板中的merge(1, 4)

python 通过字典实现的并查集按秩合并(并查集merge中size的优化) 解释及模板_第2张图片

index 1 2 3 4
father 4 4 4 None

可以发现,这个结构的树的层相对较高,
若此时元素数量增多,这样产生的消耗就会相对较大

解决这个问题其实很简单:
在进行具体指向操作的时候先进行判断
把元素少的集合根节点指向元素多的根节点
能更高概率的生成一个层数比较低的树。

构造并查集的时候需要多一个参数,
size 数组,size[i] 表示以 i 为根的集合中元素个数。
(这里如果通过字典实现并查集也需要传入数组长n)

class UnionFind:
    def __init__(self, n):
        """
        记录每个节点的父节点
        """
        self.father = {
     }
        self.size = [0] * n

在进行合并操作时
需根据两个元素所在树的元素个数不同判断合并方向
在merge中进行优化进行优化操作

具体方法
将判错功能独立出来
如果size[x]的元素个数(连通分量中的节点个数)大于size[y]中的
就将root_x, root_y进行互换再进行操作
(等价于让y的连通分量并入x为根节点的连通分量中)

    def merge(self, x, y):
        """
        合并两个节点
        """
        root_x, root_y = self.find(x), self.find(y)

        if root_x == root_y:
            return False

        if self.size[root_x] < self.size[root_y]:
            root_x, root_y = root_y, root_x

        self.size[root_x] += self.size[root_y]
        self.father[root_x] = root_y

size合并优化后结果

python 通过字典实现的并查集按秩合并(并查集merge中size的优化) 解释及模板_第3张图片

index 1 2 3 4
father None 1 1 1

完整模板:

class UnionFind:
    def __init__(self, n):
        """
        记录每个节点的父节点
        """
        self.father = {
     }
        self.size = [0] * n

    def find(self, x):
        """
        查找根节点
        路径压缩
        """
        root = x

        while self.father[root] is not None:
            root = self.father[root]

        # 路径压缩
        while x != root:
            original_father = self.father[x]
            self.father[x] = root
            x = original_father

        return root

    def merge(self, x, y):
        """
        合并两个节点
        """
        root_x, root_y = self.find(x), self.find(y)

        if root_x == root_y:
            return False

        if self.size[root_x] < self.size[root_y]:
            root_x, root_y = root_y, root_x

        self.size[root_x] += self.size[root_y]
        self.father[root_x] = root_y

    def is_connected(self, x, y):
        """
        判断两节点是否相连
        """
        return self.find(x) == self.find(y)

    def add(self, x):
        """
        添加新节点
        """
        if x not in self.father:
            self.father[x] = None

size优化的优点及不足

优点:

引入秩的概念是为了能更快的查找到根
如果不引入秩的概念
可以试想一种极坏的情况
并查集所表示树形结构退化为链
此时查找根节点效率很低
但是用秩的话就不会出现这种情况。

不足与无奈:

《算法导论》和《算法(第 4 版)》都有讲「秩」的含义
「按秩合并」又叫「启发式合并」,
「启发」的意思是:「依据经验」、「尝试」、「探测」,
在可接受误差的情况下行之有效的算法策略
简而言之:虽然不精确、达不到最优
但好过没有

你可能感兴趣的:(python,知识点记录,算法,python,数据结构,并查集,按秩合并)