QuickUnion
QuickUnion的原理较简单,每次连接两元素时找到两元素对应根节点,连接根节点即可。
class QuickUnion(object):
def __init__(self, n):
self.__parent = list(range(n))
def get_root(self, p):
p_parent = self.__parent[p]
while p_parent != self.__parent[p_parent]:
p_parent = self.__parent[p_parent]
return p_parent
def connect(self, a, b):
self.__parent[self.get_root(a)] = self.__parent[self.get_root(b)]
def is_connected(self, a, b):
return self.get_root(a) == self.get_root(b)
若树极不平衡,如1(root) -> 2 -> 3 -> 4 -> 5
,则效率与链表相同。对于含有
N
N
N个元素的并查集,时间复杂度为
Θ
(
N
)
\Theta(N)
Θ(N)。为避免这种情况出现,引入WeigtedQuickUnion。
WeigtedQuickUnion
WeigtedQuickUnion原理与QuickUnion相同,但会额外记录每棵树的大小。实际上,并查集的的效率取决于高度最大的树的高度。因此,每次连接时将较小的树(所含元素较少)连接到较大的树上。遵循此规则(注意:所有的树都是满足在此规则下生成的),在最坏的情况下,对于含有 N N N个元素的并查集,最大高度为 Θ ( log N ) \Theta(\log N) Θ(logN),因此时间复杂度为 Θ ( log N ) \Theta(\log N) Θ(logN)。
class WeightedQuickUnionDS:
def __init__(self, n):
self.__parent = list(range(n))
self.__size = [1 for _ in range(n)]
def get_root(self, p):
p_parent = self.__parent[p]
while p_parent != self.__parent[p_parent]:
p_parent = self.__parent[p_parent]
return p_parent
def connect(self, a, b):
a_root = self.get_root(a)
b_root = self.get_root(b)
if self.__size[a_root] > self.__size[b_root]:
self.__parent[b_root] = self.__parent[a_root]
self.__size[a_root] += self.__size[b_root]
else:
self.__parent[a_root] = self.__parent[b_root]
self.__size[b_root] += self.__size[a_root]
def is_connected(self, a, b):
return self.__parent[self.get_root(a)] == self.__parent[self.get_root(b)]
注意到每次查询时都需从头寻找一次根节点,可以进一步改进。
WeigtedQuickUnion With Path Compression
更进一步,在每次查询时将查询路径上所有点点直接连接到根节点上,以后查询时就不需再从头查找。
class WQUPCDS(object):
def __init__(self, n):
self.__parent = list(range(n))
self.__size = [1 for _ in range(n)]
def get_root(self, a):
a_parent = self.__parent[a]
while a_parent != self.__parent[a_parent]:
a_parent = self.__parent[a_parent]
return a_parent
def connect(self, a, b):
a_root = self.get_root(a)
b_root = self.get_root(b)
if self.__size[a_root] > self.__size[b_root]:
self.__parent[b_root] = self.__parent[a_root]
self.__size[a_root] += self.__size[b_root]
else:
self.__parent[a_root] = self.__parent[b_root]
self.__size[b_root] += self.__size[a_root]
def is_connected(self, a, b):
a_root = self.get_root(a)
b_root = self.get_root(b)
a_id = a
b_id = b
while a_id != self.__parent[a_id]:
parent = self.__parent[a_id]
self.__parent[a_id] = a_root
a_id = parent
while b_id != self.__parent[b_id]:
parent = self.__parent[b_id]
self.__parent[b_id] = b_root
b_id = parent
return a_root == b_root
该算法的时间复杂度为 lg ∗ N \lg^*{N} lg∗N。
N N N | lg ∗ N \lg^*{N} lg∗N |
---|---|
1 | 0 |
2 | 1 |
4 | 2 |
16 | 3 |
2 16 2^{16} 216 | 4 |
2 256 2^{256} 2256 | 5 |