最近公共祖先LCA(模板)

树上结点总数为n,那么借助于dfs可以实现O(n)预处理,O(n)计算任意两个结点 (u,v) 的LCA

#include<bits/stdc++.h>
using namespace std;

const int maxn=10005;

int n;
vector<int> g[maxn];    //邻接表
int root;               //树根

int parent[maxn];       //父亲结点,根结点的父亲为-1
int depth[maxn];        //结点深度

void dfs(int u,int fa,int d){
    parent[u]=fa;
    depth[u]=d;
    for(int i=0;i<g[u].size();++i){
        int v=g[u][i];
        if(v!=fa) dfs(v,u,d+1);
    }
}

int LCA(int u,int v){   //计算u,v的最近公共祖先 O(n)
    while(depth[u]>depth[v]) u=parent[u];
    while(depth[v]>depth[u]) v=parent[v];
    while(u!=v){
        u=parent[u];
        v=parent[v];
    }
    return u;
}

int main(){
    while(scanf("%d",&n)==1 && n){
        for(int i=0;i<n-1;++i){
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        root=1;
        dfs(root,-1,0);     //预处理 O(n)

        int q;
        scanf("%d",&q);
        while(q--){
            int u,v;
            scanf("%d%d",&u,&v);
            int ans=LCA(u,v);
            printf("%d\n",ans);
        }
    }
    return 0;
}
/*
8
1 2
1 3
2 4
2 5
3 6
5 7
5 8
*/

借助于二分搜索,可以实现 O(nlogn) 的预处理,然后在O(logn) 的时间内计算出任意两结点的LCA

#include<bits/stdc++.h>
using namespace std;

const int maxn=10005;
const int max_logn=30;

int n;                          //树上的结点数(下标从0开始)
vector<int> g[maxn];            //邻接表
int root;                       //树根

int parent[max_logn][maxn];     //向上走2^k步所到达的结点(超过根时为-1)
int depth[maxn];

void dfs(int u,int fa,int d){
    parent[0][u]=fa;
    depth[u]=d;
    for(int i=0;i<g[u].size();++i){
        int v=g[u][i];
        if(v!=fa) dfs(v,u,d+1);
    }
}

void init(){      //预处理 O(nlogn)
    dfs(root,-1,0);  //预处理parent[0]和depth
    for(int k=0;k+1<max_logn;++k){
        for(int u=0;u<n;++u){
            if(parent[k][u]<0) parent[k+1][u]=-1;
            else parent[k+1][u]=parent[k][parent[k][u]];
        }
    }
}

int LCA(int u,int v){   //计算LCA(u,v) O(logn)
    if(depth[u]>depth[v]) swap(u,v);
    for(int k=0;k<max_logn;++k){
        if((depth[v]-depth[u])>>k&1){
            v=parent[k][v];
        }
    }
    if(u==v) return u;
    for(int k=max_logn-1;k>=0;--k){
        if(parent[k][u]!=parent[k][v]){
            u=parent[k][u];
            v=parent[k][v];
        }
    }
    return parent[0][u];
}

int main(){
    while(scanf("%d",&n)==1 && n){
        for(int i=0;i<n-1;++i){
            int u,v;
            scanf("%d%d",&u,&v);
            --u,--v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        root=0;
        init();
        int q;
        scanf("%d",&q);
        while(q--){
            int u,v;
            scanf("%d%d",&u,&v);
            --u,--v;
            int ans=LCA(u,v)+1;
            printf("%d\n",ans);
        }
    }
    return 0;
}

还有基于RMQ和dfs序的算法,O(nlogn)的预处理,O(1)计算

#include<bits/stdc++.h>
using namespace std;

const int maxn=10005;

int n;                  //顶点个数(下标从0开始)
vector<int> g[maxn];    //邻接表
int root;

int vs[maxn<<1];        //dfs的访问顺序
int depth[maxn<<1];     //结点深度
int id[maxn];           //各个顶点在vs中首次出现的下标
int MinId[maxn<<1][30]; //最小值下标的S-T表

void RMQ_init(){
    int len=n<<1;
    for(int i=0;i<len;++i) MinId[i][0]=i;
    for(int j=1;(1<<j)<=len;++j){
        for(int i=0;i+(1<<j)-1<len;++i){
            int x=depth[MinId[i][j-1]],y=depth[MinId[i+(1<<(j-1))][j-1]];
            if(x<=y)
                MinId[i][j]=MinId[i][j-1];
            else
                MinId[i][j]=MinId[i+(1<<(j-1))][j-1];
        }
    }
}

int RMQ_MinId(int L,int R){
    int k=(int)(log((R-L+1)*1.0)/log(2.0));
    if(depth[MinId[L][k]]<=depth[MinId[R-(1<<k)+1][k]])
        return MinId[L][k];
    else
        return MinId[R-(1<<k)+1][k];
}

void dfs(int u,int fa,int d,int &k){
    id[u]=k;
    vs[k]=u;
    depth[k++]=d;
    for(int i=0;i<g[u].size();++i){
        int v=g[u][i];
        if(v!=fa){
            dfs(v,u,d+1,k);
            vs[k]=u;
            depth[k++]=d;
        }
    }
}

void init(){            //预处理
    int k=0;
    dfs(root,-1,0,k);   //处理vs,depth,id
    RMQ_init();         //建立S-T表
}

int LCA(int u,int v){
    int a=min(id[u],id[v]);
    int b=max(id[u],id[v]);
    return vs[RMQ_MinId(a,b)];
}

int main(){
     while(scanf("%d",&n)==1 && n){
        for(int i=0;i<n-1;++i){
            int u,v;
            scanf("%d%d",&u,&v);
            --u,--v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        root=0;
        init();
        int q;
        scanf("%d",&q);
        while(q--){
            int u,v;
            scanf("%d%d",&u,&v);
            --u,--v;
            int ans=LCA(u,v)+1;
            printf("%d\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xiao_k666/article/details/82080489