Tarjan 算法求LCA(Least Common Ancestors)

  Tarjan有几个著名的算法,其中之一就是求LCA(最近公共祖先)。
  一个出现在有根树中间的常见问题是:在有根树T中询问一个距离根节点最远的结点 x (即深度最大的结点)使得x同时为结点 TuTv 的祖先,我们称 x uv的最近公共祖先。这种对于公共祖先的询问我们称之为公共祖先问题,并且以 LCA(T,u,v) 表示询问 T 中结点TuTv的最近公共祖先。
  考虑一个简单的有根树T(如图):我们有:
   LCA(T,1,3)=T2
   LCA(T,3,4)=T4
   LCA(T,3,5)=T4

  
  Tarjan算法的原理是通过求根到 u 的路径中与v最近的点。
  而这种方式正好与深度优先搜索(DFS)的路径有一定的联系,即这个点为搜索过程中恰好经过一次且深度最小的点。因此可以转化为RMQ问题利用ST算法求解。
  
  Tarjan算法则直接利用深搜的特点进行计算,具体过程为:
  TarjanDFS(u):
  1.以 u 为代表元建立一个集合
  2.对于每个u的子树 v ,进行TarjanDFS(v)搜索,然后将子树v的集合与 u 的集合合并,代表元仍为u
  3.将 u 设为已遍历过
  4.对于每组有关u的询问 LCA(T,u,v) ,若 v 已遍历过,则答案为v所在集合代表元
  也可以参考Tarjan’s off-line lowest common ancestors algorithm或者tarjan_LCA_算法最佳理解文档
  这样我们就可以得到一个时间复杂度为 O(Nα(N)+Q) α(N) 为反阿克曼函数,一般不大于5; Q <script type="math/tex" id="MathJax-Element-27">Q</script>为询问次数)的算法了。
  
  伪代码如下(wikipedia_en):

function TarjanOLCA(u)
     MakeSet(u);
     u.ancestor := u;
     for each v in u.children do
         TarjanOLCA(v);
         Union(u,v);
         Find(u).ancestor := u;
     u.colour := black;
     for each v such that {u,v} in P do
         if v.colour == black
             print "Tarjan's Lowest Common Ancestor of " + u +
                   " and " + v + " is " + Find(v).ancestor + ".";
function MakeSet(x)
     x.parent := x
     x.rank   := 0

 function Union(x, y)
     xRoot := Find(x)
     yRoot := Find(y)
     if xRoot.rank > yRoot.rank
         yRoot.parent := xRoot
     else if xRoot.rank < yRoot.rank
         xRoot.parent := yRoot
     else if xRoot != yRoot
         yRoot.parent := xRoot
         xRoot.rank := xRoot.rank + 1

 function Find(x)
     if x.parent == x
        return x
     else
        x.parent := Find(x.parent)
        return x.parent

参考资料:
1.Tarjan’s off-line lowest common ancestors algorithm
2.tarjan_LCA_算法最佳理解文档
3.郭华阳《RMQ与LCA问题》(2007)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值