树型集合的LINK和FIND-DEPTH操作
问题描述:
考虑如下两类操作:
LINK(v,r): v是一棵树中的结点,r是另一棵树的根,Link的执行使得r成为v的子结点,从而实现两树的合并。
FIND-DEPTH(v):求出结点v的当前深度。
现在的问题是,对于若干个集合1……n,集合的数据结构采用树来表示,对于这些集合可以进行以上两类操作,现在考虑设计一个性能良好的算法来实现这两类操作。特别对于编译器的实现是有十分大得帮助的。
问题解决:
FIND-DEPTH的执行:
如果采用一般的树型结构的话,FIND-DEPTH操作可能面对的集合的树结构退化成了链表,所以最坏情况下执行O(n)条FIND-DEPTH指令的时间复杂度可能是O(n2)。正如上篇文章所述的,这里为了使得性能更好,我们需要进行路径压缩。路径压缩简单来讲就是将一个节点到根节点的路径上的所有节点都直接指向根节点。
但是,问题来了,当使用路径压缩的时候,整个树中的节点(除了根节点)的高度都发生了变化,这又导致了FIND-DEPTH的结果出错。并且,使用路径压缩还对LINK指令有影响,由于以r为根的子树中结点的深度全部发生了变化,则势必要修改该子树中所有结点的深度字段。此工作量很大,最坏情况下,O(n)条LINK指令的时间复杂度也为O(n2)。看来,这里不仅仅需要路径压缩,还需要一些辅助的措施。
具体的方法:给每个结点v增加2个字段(Count[v]和Weight[v]),并把改造过的此类结点和树所构成的森林称为D-森林。(下文讨论的操作都是在D-森林中进行的。)
Count[v]: 记录D-森林中以v为根的子树中结点的个数。
Weight[v]满足下述性质:设在D-森林中执行指令Find-Depth(v),从v找到根结点a路径上的结点是vi1(=v)vi2vi3…vik(=a), 则等于v在原树中的深度。
在上例中,对左图中两子树执行LINK(r,u),在原先的森林里就成为了中图的形状; 为减小时间复杂度,实际执行时,LINK(r,u)不在原先的森林里执行,