浅谈Tarjan——5(用Tarjan求LCA)

来源:互联网 发布:Mac怎么改变照片日期 编辑:程序博客网 时间:2024/06/05 21:58

Tarjan算法不仅可以用来缩点,还可以用来求LCA。

先介绍一下LCA,LCA(least common ancestors),是最近公共祖先的英文。指的是在一棵树中,点u和点v的最近的祖先。这里写图片描述
如图,以黄点为树根:紫点和黑点的最近公共祖先就是绿点,白点和橙点的最近公共祖先就是橙点,灰点和蓝点的最近公共祖先是黄点。简而言之,LCA就是点u和v的父亲的父亲的父亲……第一个相交的父亲。

LCA的算法有很多,例如倍增RMQ,树剖。用Tarjan算法来求LCA,理论上时间复杂度为(N+Q),但在实际操作中会稍稍慢一些。Tarjan求LCA是一个离线算法,并基于并查集(union-find-set)。

我们用vis记录该点是否被访问过,fa记录该点的父亲,从根节点向下搜索,遍历每一个儿子。如果它的儿子全都遍历完了,就去查询与该点有关的点,也就是所查询的点组中有该点的那些组,如果另一个点v已经被访问过,那么这两个点的最近公共祖先就是find(v),否则先不记录。(证明略)
基本思路就是这五步:

1.从根节点开始搜索。

2.遍历该点u所有子节点v,并标记这些子节点v已被访问过。

3.若是v还有子节点,遍历v的子节点,否则合并u,v。

4.寻找与当前点u有询问关系的点v。

5.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。
伪代码:

void lca(int x){    now[x]=1;    for x的所有儿子{        lca(v);        vis[v]=1;fa[v]=x;     }    for 与x有关的查询        if(vis[v])          ans=find(v);}

模板:

void lca(int x){    now[x]=1;    for(int i=0;i<f[x].size();i++){        int go=f[x][i];        if(now[go]) continue;        lca(go);        vis[go]=1;fa[go]=x;     }    for(int i=0;i<query[x].size();i++){        int go=query[x][i];        if(vis[go]) ans=find(go);    }}

详细模拟过程点这里,这位神犇写的很好

原创粉丝点击