我们可以做两件事来改进简单和次优不相交集子算法:
路径压缩启发式:findSet不需要处理高度大于的树2。如果最终迭代了这样的树,它可以将较低的节点直接链接到根,从而优化将来的遍历。
subalgo findSet(v: a node):
ifv.parent!= v
v.parent= findSet(v.parent)
return v.parent
基于高度的合并启发式:对于每个节点,存储其子树的高度。合并时,将较高的树作为较小树的父树,这样就不会增加任何人的身高。
subalgo unionSet(u, v: nodes):
vRoot = findSet(v)
uRoot = findSet(u)
if vRoot == uRoot:
return
ifvRoot.height< uRoot.height:
vRoot.parent= uRoot
else ifvRoot.height> uRoot.height:
uRoot.parent= vRoot
else:
uRoot.parent= vRoot
uRoot.height= uRoot.height+ 1
这会导致每次操作都花费时间,而这是快速增长的阿克曼函数的逆函数,因此增长非常缓慢,可以考虑用于实际目的。O(alpha(n))alphaO(1)
由于初始排序,因此构成了整个Kruskal的算法。O(m log m + m) = O(m log m)
注意
路径压缩可能会降低树的高度,因此在联合操作期间比较树的高度可能不是一件容易的事。因此,为了避免存储和计算树的高度的复杂性,可以随机选择生成的父级:
subalgo unionSet(u, v: nodes): vRoot = findSet(v) uRoot = findSet(u) if vRoot == uRoot: return if random() % 2 == 0: vRoot.parent= uRoot else: uRoot.parent= vRoot
在实践中,这种随机算法与用于findSet操作的路径压缩一起将产生可比的性能,但实现起来要简单得多。