二叉树最近共同祖先问题
最近共同祖先问题 | ||||||||||||
Union/Find 数据结构的一个例子是离线的最近共同祖先(NCA) 问题。 |
||||||||||||
给定一棵树和树中节点对的列表,对每一个节点对需找到其最近的共同祖先。 | ||||||||||||
|
||||||||||||
算法描述 该算法通过执行树的后根遍历来实现. 当我们准备从处理一个节点中返回时,我们要检查对偶列表看看是否有任何关于它的祖先计算要被执行. 如果u为当前节点,(u,v)在对偶列表中,并且我们已经完成了对v的递归调用,那么我们就有足够的信息来决定NCA(u,v) 。 |
||||||||||||
|
||||||||||||
|
||||||||||||
|
||||||||||||
为了说明方便,我们引入一个新的概念——锚(anchor) | ||||||||||||
一个被访问过的节点v的锚 即为在当前访问路径上最靠近v的节点. 在图中,p的锚是A,q的锚是B,r尚未被锚定因为其还未被访问过,基于r是首次被访问我们认为r的锚是其本身. 如图所示,在当前访问路径上的每个节点都是一个锚(至少是其本身的),而且,被访问过的节点构成了一个个等价类:如果两个节点拥有共同的锚那么这两个节点是相关的,并且我们认为每个未被访问过的节点处于自己构成的类中. |
||||||||||||
1. 如果以各子树的根节点来命名其所在的等价类,能否说一个节点的锚即为其当前所在等价类的名称? |
||||||||||||
现在,再次假定(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