数据结构之并查集:路径压缩继续优化并查集——20

路径压缩继续优化并查集

在实现的并查集中,在合并操作merge(item1, item2)时,会不管两个元素所在的分组大小,总是将item
1的分组合并到item2的分组,这样可能会导致树的深度无必要地增加:
数据结构之并查集:路径压缩继续优化并查集——20_第1张图片
如果是大树合并到小树上,会导致树的深度增加,进而造成增删改查等操作的代价增大
因此我们要对并查集的合并操作进行优化。

优化合并方式

  • 每次合并时,都将小分组的元素往大分组合并,由于本例初始化都是一个元素,对应一个分组,它合并的情况可能会变成如下这样(箭头代表分组之间的edge),类似一颗星状的树:
    数据结构之并查集:路径压缩继续优化并查集——20_第2张图片

属性和方法说明

  1. self.num_groups 分组数量
  2. self.groups 一个数组,索引代表传入的item的值,元素代表分组编号
  3. nums_in_each_group 一个数组,索引代表分组编号,元素代表每个分组的元素个数
  4. count_groups() 获取当前总共分数的数量
  5. in_the_same_group(item1, item2) 判断两个传入的item是否在同一分组
  6. which_group(item) 获取传入的item的所在分组
  7. unite(item1, item2) 合并两个元素到同一分组,小的分组总是会往大的分组合并

Python代码实现及测试

class UF_Tree_Weighted:
    def __init__(self, n):
        self.num_groups = n
        self.groups = [i for i in range(n)]
        self.nums_in_each_group = [1 for _ in range(n)]

    def count_groups(self):
        return self.num_groups

    def in_the_same_group(self, item1, item2):
        return self.which_group(item1) == self.which_group(item2)

    def which_group(self, item):
        """Find item's root------>groups[groups[groups[...groups[item]...]]]"""
        while self.groups[item] != item:
            item = self.groups[item]
        return item

    def unite(self, item1, item2):
        p = self.which_group(item1)
        q = self.which_group(item2)
        if p == q:
            return
        if self.nums_in_each_group[p] <= self.nums_in_each_group[q]:
            # Merge the smaller group into the bigger's
            self.nums_in_each_group[q] += self.nums_in_each_group[p]
            self.nums_in_each_group[p] = 0
            # Change the smaller group-number to the bigger's group-number
            self.groups[p] = self.groups[q]
        else:
            # Merge the smaller group into the bigger's
            self.nums_in_each_group[p] += self.nums_in_each_group[q]
            self.nums_in_each_group[q] = 0
            self.groups[q] = self.groups[p]
        # Numbers of group subtract 1
        self.num_groups -= 1


if __name__ == '__main__':
    UF = UF_Tree_Weighted(5)

    print(f"The initial number of groups is {UF.num_groups}")

    while True:
        p = int(input(f'Input the to-be-merge element: '))
        q = int(input(f"Merge to the target element's group: "))

        if UF.in_the_same_group(p, q):
            print(f"They are already in the same group")
            continue
        UF.unite(p, q)
        print(f"The number of groups now is {UF.count_groups()}")
        print(UF.groups)
        print(f"Elements in each group: {UF.nums_in_each_group}")

运行结果

The initial number of groups is 5
Input the to-be-merge element: 0
Merge to the target element's group: 1
The number of groups now is 4
[1, 1, 2, 3, 4]
Elements in each group: [0, 2, 1, 1, 1]
Input the to-be-merge element: 1
Merge to the target element's group: 2
The number of groups now is 3
[1, 1, 1, 3, 4]
Elements in each group: [0, 3, 0, 1, 1]
Input the to-be-merge element: 3
Merge to the target element's group: 2
The number of groups now is 2
[1, 1, 1, 1, 4]
Elements in each group: [0, 4, 0, 0, 1]
Input the to-be-merge element: 2
Merge to the target element's group: 4
The number of groups now is 1
[1, 1, 1, 1, 1]
Elements in each group: [0, 5, 0, 0, 0]
Input the to-be-merge element: 

当调用unite()合并时,合并时不考虑参数的先后位置,而是考虑分组的大小,总是会有小的分组合并到大的分组

你可能感兴趣的:(数据结构)