Tarjan有几个著名的算法,其中之一就是求LCA(最近公共祖先)。
一个出现在有根树中间的常见问题是:在有根树T中询问一个距离根节点最远的结点
x
(即深度最大的结点)使得
考虑一个简单的有根树T(如图):我们有:
LCA(T,1,3)=T2
LCA(T,3,4)=T4
LCA(T,3,5)=T4
Tarjan算法的原理是通过求根到
u
的路径中与
而这种方式正好与深度优先搜索(DFS)的路径有一定的联系,即这个点为搜索过程中恰好经过一次且深度最小的点。因此可以转化为RMQ问题利用ST算法求解。
Tarjan算法则直接利用深搜的特点进行计算,具体过程为:
TarjanDFS(u):
1.以
u
为代表元建立一个集合
2.对于每个
3.将
u
设为已遍历过
4.对于每组有关
也可以参考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)