最近公共祖先(Tarjan算法)

来源:互联网 发布:猎鲸狂人软件 编辑:程序博客网 时间:2024/06/06 08:32

最近公共祖先(Tarjan离线算法)

当面对一个图中要求任意两点间的最短路,用kruskal或者prim算法显然会很耗时间。利用最近公共祖先我们就非常的方便快捷。

算法复杂度:

LCA的离线算法。复杂度为O(n+q)。

算法分析:

这个算法充分利用了dfs树的结构。

对于每个节点u,关于它的询问(u,v)只有两种。(假设先dfs(u)后dfs(v))


1、v在u的子树内

此时LCA(u,v) = u.

2、v不在u的子树内

⑴假设v在u的父亲的另一棵子树内。

此时LCA(u,v) = father[u].

⑵如果不满足条件⑴,则v可能在u的父亲的父亲的另一棵子树内。

而此时LCA(u,v) = father[ father[u] ].

⑶……


观察一下,是不是发现了什么呢?

没错,不论是哪种情况,LCA(u,v)都与u和father[ ]有某种关系。我们能不能抓住这种关系呢?

我们继续观察,一直向上取father[ ],貌似和并查集的FIND操作很像呢。


我们用并查集的角度依次考虑上面的情况试试看。

1、v在u的子树内

此时dfs(u)还在栈中,没有执行完,此时没有向上取father[ ],说明此时u是根。

2、v不在u的子树内

⑴假设v在u的父亲的另一棵子树内。

此时的dfs(u)已经执行完并出栈。此时向上取了一次father[ ],说明此时u的父亲是根。

⑵如果不满足条件⑴,则v可能在u的父亲的父亲的另一棵子树内。

同理,此时dfs(u的父亲)也已经执行完并出栈。此时向上取了两次father[ ],说明此时u的父亲的父亲是根。

⑶……


综上,我们只要保证当dfs(u)在栈中的时候,u是根;当dfs(u)不在栈中的时候,father[u]是根就行了。


#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define FileIn  freopen("in.ads","r",stdin)#define FileOut freopen("out.ads","w",stdout)#define N 155#define M 22555struct Vertex{    int head;}V[N],Qv[N];struct Edge{    int v,next;}E[M],Qe[M];int top,pre[N];bool used[N];void Init(){    top = 0;    memset(V,-1,sizeof(V));    memset(Qv,-1,sizeof(Qv));    memset(pre,-1,sizeof(pre));    memset(used,0,sizeof(used));}int Root(int x){    if(pre[x] != -1)        return pre[x] = Root(pre[x]);    else        return x;}void Union(int a,int b){    int r1 = Root(a);    int r2 = Root(b);    if(r1 != r2)        pre[r2] = r1;}void Add_Edge(int u,int v){    E[top].v = v;    E[top].next = V[u].head;    V[u].head = top++;    /*printf("E[%d].v = %d\n", top-1, v);    printf("E[%d].next = V[%d].head\n",top-1, u);    printf("V[%d].head = %d++\n", u, top-1);*/}void Add_Qedge(int u,int v){    Qe[top].v = v;    Qe[top].next = Qv[u].head;    Qv[u].head = top++;}void Tarjan(int u){    used[u] = true;    for(int i=Qv[u].head;i!=-1;i=Qe[i].next)    {        int v = Qe[i].v;        if(used[v])            printf("The LCA of (%d,%d) is -> %d\n",u,v,Root(v));    }    for(int i=V[u].head;i!=-1;i=E[i].next)    {        int v= E[i].v;        if(used[v])            continue;        Tarjan(v);        Union(u,v);    }}int main(){    //FileIn;    int n,m,u,v,Q;    while(~scanf("%d%d",&n,&m))    {        Init();        while(m--)        {            scanf("%d%d",&u,&v);            Add_Edge(u,v);            Add_Edge(v,u);        }        scanf("%d",&Q);        while(Q--)        {            scanf("%d%d",&u,&v);            Add_Qedge(u,v);            Add_Qedge(v,u);        }        Tarjan(1);    }return 0;}


0 0