二叉树最近共同祖先问题

最近共同祖先问题

       Union/Find 数据结构的一个例子是离线的最近共同祖先(NCA) 问题。
  给定一棵树和树中节点对的列表,对每一个节点对需找到其最近的共同祖先。

图中显示了一棵树,其带有一个含5个请求的对偶列表。对于节点对u和z,节点C是最近的共同祖先(思考 A和B是否满足条件?)。该问题为离线的,因为我们在提供第一个答案之前就能看到整个请求序列 。

     算法描述   该算法通过执行树的后根遍历来实现. 当我们准备从处理一个节点中返回时,我们要检查对偶列表看看是否有任何关于它的祖先计算要被执行. 如果u为当前节点,(u,v)在对偶列表中,并且我们已经完成了对v的递归调用,那么我们就有足够的信息来决定NCA(u,v) 。

为什么会有这个结论呢?带着这个问题,我们继续向下看。

为了理解该算法是如何运行的,让我们来观察图。这里将要实现对D的递归调用. 在图中:

所有标记有阴影的节点都已经在一次递归调用中被访问过;

除了通向D路径上的所有节点,所有的递归调用都已经被完成. 当对一个节点的递归调用完成后,我们就对该节点做好标记;

如果v被标记了,那么NCA(D,v)就是通往D路径上的某个节点;

结合后根遍历,被访问的节点是否一定被标记?
  为了说明方便,我们引入一个新的概念——锚(anchor)

   一个被访问过的节点v的锚 即为在当前访问路径上最靠近v的节点. 在图中,p的锚是A,q的锚是B,r尚未被锚定因为其还未被访问过,基于r是首次被访问我们认为r的锚是其本身. 如图所示,在当前访问路径上的每个节点都是一个锚(至少是其本身的),而且,被访问过的节点构成了一个个等价类:如果两个节点拥有共同的锚那么这两个节点是相关的,并且我们认为每个未被访问过的节点处于自己构成的类中.

    1.  如果以各子树的根节点来命名其所在的等价类,能否说一个节点的锚即为其当前所在等价类的名称? 
    2.一个节点的锚是固定不变的吗?

 现在,再次假定(D,v)在对偶列表中,那么我们有以下三种情况:

v未被标记,我们就无法计算NCA(D,v).

 v被标记且它在D的子树中,这时NCA(v,D)=D.

 v被标记但它不在D的子树中,这时NCA(v,D)就等于v的锚。

    综上,就是要确保在任何情况下,我们都能确定任一被访问节点的锚. 通过使用Union/Find算法很容易做到这一点. 一次递归调用返回后,我们调用Union,例如,在上图中,对D的递归调用返回后,所有D中的节点都使它们的锚由D变为C,这时我们就需要将两个等价类合并成一个. 在任何一点,我们都能够通过调用Find操作来得到顶点v的锚. 因为Find返回一个集合序号,这样我们就可以使用数组Anchor来存储对应于特定集合的锚节点.


另外,还可参考:

http://blog.csdn.net/zhaohuiy/article/details/4660877

http://blog.csdn.net/yinxusen/article/details/6265524

posted @ 2011-09-20 23:21  夏至冬末  阅读(257)  评论(0编辑  收藏  举报