python实现一个简单的并查集

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

并查集有三种基本操做,得到根节点,判断两节点是否连通,以及将两不连通的节点相连(至关于将两节点各自的集合合并)数组

用UnionFind类来表示一个并查集,在构造函数中,初始化一个数组parent,parent[i]表示的含义为,索引为i的节点,它的直接父节点为parent[i]。初始化时各个节点都不相连,所以初始化parent[i]=i,让本身成为本身的父节点,从而实现各节点不互连。数据结构

def __init__(self, n):
        self.parent = list(range(n))

因为parent[i]仅表示本身的直接父节点,查询两个节点是否相交须要比较它们的根节点是否相同。所以要封装一个查询本身根节点的方法。函数

def get_root(self, i):
        while i != self.parent[i]:
            i = self.parent[i]

        return i

接下来能够经过来比较根节点是否相同来判断两节点是否连通。性能

def is_connected(self, i, j):
        return self.get_root(i) == self.get_root(j)

当要连通两个节点时,咱们要将其中一个节点的根节点的parent,设置为另外一个节点的根节点。注意,连通两个节点并不是仅仅让两节点自身相连,其实是让它们所属的集合实现合并。优化

def union(self, i, j):
        i_root = self.get_root(i)
        j_root = self.get_root(j)
        self.parent[i_root] = j_root

接下来咱们作两个小优化。
因为调用get_root时须要经过不断找本身的直接父节点,来寻找根节点,若是这棵树的层级过深,会致使性能受到严重影响。所以咱们须要在union时,尽量的减少合并后的树的高度。
在构造函数中新建一个数组rank,rank[i]表示节点i所在的集合的树的高度。code

所以,当合并树时,分别得到节点i和节点j的root i_root和j_root以后,咱们经过访问rank[i_root]和rank[j_root]来比较两棵树的高度,将高度较小的那棵连到高度较高的那棵上。若是高度相等,则能够随便,并将rank值加一。递归

def union(self, i, j):
        i_root = self.get_root(i)
        j_root = self.get_root(j)

        if self.rank[i_root] == self.rank[j_root]:
            self.parent[i_root] = j_root
            self.rank[j_root] += 1
        elif self.rank[i_root] > self.rank[j_root]:
            self.parent[j_root] = i_root
        else:
            self.parent[i_root] = j_root

经过对union操做的改良能够防止树的高度太高。咱们还能够对get_root操做自己进行优化。
当前每次执行get_root时,须要一层一层的找到本身的父节点,很费时。因为根节点没有父节点,而且文章开始处提到过若是一个节点没有父节点,那么它的父节点就是本身,所以能够说只有根节点的父节点是本身自己。如今咱们加上一个判断,判断当前节点的父节点是否为根节点,若是不为根节点,就递归地将本身的父节点设置为根节点,最后返回本身的父节点。索引

def get_root(self, i):
        if self.parent[i] != self.parent[self.parent[i]]:
            self.parent[i] = self.get_root(self.parent[i])
        return self.parent[i]

以上是python实现一个简单的并查集的方式。get

相关文章
相关标签/搜索