求最近公共祖先(LCA)的三种方法总结(Tarjan/倍增/树链剖分)

版权声明:转载请告知博主并要注明出处嗷~ https://blog.csdn.net/Akatsuki__Itachi/article/details/82841223

以模板题目poj1330为例

Description

A rooted tree is a well-known data structure in computer science and engineering. An example is shown below: 

 
In the figure, each node is labeled with an integer from {1, 2,...,16}. Node 8 is the root of the tree. Node x is an ancestor of node y if node x is in the path between the root and node y. For example, node 4 is an ancestor of node 16. Node 10 is also an ancestor of node 16. As a matter of fact, nodes 8, 4, 10, and 16 are the ancestors of node 16. Remember that a node is an ancestor of itself. Nodes 8, 4, 6, and 7 are the ancestors of node 7. A node x is called a common ancestor of two different nodes y and z if node x is an ancestor of node y and an ancestor of node z. Thus, nodes 8 and 4 are the common ancestors of nodes 16 and 7. A node x is called the nearest common ancestor of nodes y and z if x is a common ancestor of y and z and nearest to y and z among their common ancestors. Hence, the nearest common ancestor of nodes 16 and 7 is node 4. Node 4 is nearer to nodes 16 and 7 than node 8 is. 

For other examples, the nearest common ancestor of nodes 2 and 3 is node 10, the nearest common ancestor of nodes 6 and 13 is node 8, and the nearest common ancestor of nodes 4 and 12 is node 4. In the last example, if y is an ancestor of z, then the nearest common ancestor of y and z is y. 

Write a program that finds the nearest common ancestor of two distinct nodes in a tree. 
 

Input

The input consists of T test cases. The number of test cases (T) is given in the first line of the input file. Each test case starts with a line containing an integer N , the number of nodes in a tree, 2<=N<=10,000. The nodes are labeled with integers 1, 2,..., N. Each of the next N -1 lines contains a pair of integers that represent an edge --the first integer is the parent node of the second integer. Note that a tree with N nodes has exactly N - 1 edges. The last line of each test case contains two distinct integers whose nearest common ancestor is to be computed.

Output

Print exactly one line for each test case. The line should contain the integer that is the nearest common ancestor.

Sample Input

2
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
16 7
5
2 3
3 4
3 1
1 5
3 5

Sample Output

4
3

①Tarjan离线算法:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#define eps 1e-8
#define memset(a,v) memset(a,v,sizeof(a))
using namespace std;
typedef long long int LL;
const int MAXL(1e6);
const int INF(0x7f7f7f7f);
const int mod(1e9+7);
int dir[4][2]= {{-1,0},{1,0},{0,1},{0,-1}};
struct node
{
    int to;
    int next;
}edge[MAXL+50];
int head[MAXL+50];
int father[MAXL+50];
bool vis[MAXL+50];
bool is_root[MAXL+50];
int n;
int cnt;
int cx,cy;
int ans;
int root;


int Find(int x)
{
    if(x!=father[x])
        father[x]=Find(father[x]);
    return father[x];
}

void Join(int x,int y)
{
    int fx=Find(x),fy=Find(y);
    if(fx!=fy)
        father[fy]=fx;
}

void add_edge(int x,int y)
{
    edge[cnt].to=y;
    edge[cnt].next=head[x];
    head[x]=cnt++;
}

void init()
{
    cnt=0;
    memset(head,-1);
    memset(vis,false);
    memset(is_root,true);
    scanf("%d",&n);
    for(int i=0;i<=n;i++)
        father[i]=i;
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add_edge(x,y);
        is_root[y]=false;
    }
    for(int i=1;i<=n;i++)
        if(is_root[i]==true)
            root=i;
}

void LCA(int u)
{
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        LCA(v);
        Join(u,v);
        vis[v]=true;

    }
    if(cx==u&&vis[cy]==true)
        ans=Find(cy);
    if(cy==u&&vis[cx]==true)
        ans=Find(cx);
}
void solve()
{
    scanf("%d%d",&cx,&cy);
    LCA(root);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        solve();
        cout<<ans<<endl;
    }
}

②倍增法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#define eps 1e-8
#define swap(a,b)  (a=a+b,b=a-b,a=a-b)
#define memset(a,v) memset(a,v,sizeof(a))
using namespace std;
typedef long long int LL;
const int MAXL(1e4);
const int INF(0x7f7f7f7f);
const int mod(1e9+7);
int dir[4][2]= {{-1,0},{1,0},{0,1},{0,-1}};
struct node{
    int to,next;
    node(){}
    node(int to,int next):to(to),next(next){}
}edge[2*MAXL+50];
int head[2*MAXL+50];
int depth[MAXL+50];
int dp[MAXL+50][30];
bool vis[MAXL+50];
bool is_root[MAXL+50];
int n,q,cnt,root;

void init(){
    cnt=0;
    memset(head,-1);
    memset(dp,0);
    memset(depth,0);
    memset(vis,false);
    memset(is_root,true);
}

void add_edge(int x,int y){
    edge[cnt]=node(y,head[x]);
    head[x]=cnt++;
}

void getDepth(int u){
    vis[u]=true;
    for(int i=head[u];~i;i=edge[i].next){
        int to=edge[i].to;
        if(!vis[to]){
            depth[to]=depth[u]+1;
            dp[to][0]=u;
            getDepth(to);
        }
    }
}

void getDp(){
    for(int up=1;(1<<up)<=n;up++){
        for(int i=1;i<=n;i++){
            dp[i][up]=dp[dp[i][up-1]][up-1];
        }
    }
}

int LCA(int x,int y){
    if(depth[x]<depth[y])
        swap(x,y);
    int i=-1;
    while((1<<(i+1))<=depth[x])
        i++;
    for(int j=i;j>=0;j--)
        if(depth[x]-(1<<j)>=depth[y])
            x=dp[x][j];
    if(x==y) return x;
    for(int j=i;j>=0;j--){
        if(dp[x][j]!=dp[y][j]){
            x=dp[x][j];
            y=dp[y][j];
        }
    }
    return dp[x][0];
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d",&n);
        for(int i=1;i<n;i++){
            int x,y,z;
            scanf("%d%d",&x,&y);
            add_edge(x,y);
            is_root[y]=false;
        }
        for(int i=1;i<=n;i++)
            if(is_root[i]==true)
                root=i;
        depth[root]=1;
        getDepth(root);
        getDp();
        int x,y;
        scanf("%d%d",&x,&y);
        int lca=LCA(x,y);
        cout<<lca<<endl;
    }
}

③树链剖分

当把树划分出轻重链之后,我们只需要不停的更换节点的top值,使之相同,最后深度较小的节点即为LCA

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#define memset(a,v)  memset(a,v,sizeof(a))
#define max(a,b)   (a>b?a:b)
#define min(a,b)   (a<b?a:b)
#define swap(a,b)  (a=a+b,b=a-b,a=a-b)
#define eps 1.0E-8
using namespace std;
const int MAXL(5*1e4);
const int INF(0x7f7f7f7f);
const int mod(1e9+7);
typedef long long int LL;
struct node
{
    int to,next;
    node(){}
    node(int to,int next):to(to),next(next){}
}edge[2*MAXL+50];
int head[MAXL+50];
int top[MAXL+50];
int father[MAXL+50];
int depth[MAXL+50];
int siz[MAXL+50];
int son[MAXL+50];
int pos[MAXL+50];
int repos[MAXL+50];
bool is_root[MAXL+50];
int tot,dpos,cnt;
int n,root;

void init()
{
    cnt=0,dpos=1;
    memset(head,-1);
    memset(son,-1);
    memset(is_root,true);
}
inline void dfs1(int u,int fa,int dep)
{
    depth[u]=dep;father[u]=fa;siz[u]=1;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v!=fa)
        {
            dfs1(v,u,dep+1);
            siz[u]+=siz[v];
            if(son[u]==-1||siz[v]>siz[son[u]])
                son[u]=v;
        }
    }
}

inline void dfs2(int u,int t)
{
    top[u]=t;
    pos[u]=dpos;
    repos[dpos]=u;
    dpos++;
    if(son[u]==-1)
        return;
    dfs2(son[u],t);
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v!=son[u]&&v!=father[u])
            dfs2(v,v);
    }
}

void add_edge(int u,int v)
{
    edge[cnt]=node(v,head[u]);
    head[u]=cnt++;
}

int LCA(int u,int v)
{
    int fu=top[u],fv=top[v];
    while(fu!=fv)
    {
        if(depth[fu]<depth[fv])
            swap(fu,fv),swap(u,v);
        u=father[fu];
        fu=top[u];
    }
    if(depth[u]>depth[v]) swap(u,v);
    return u;
}


int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d",&n);
        for(int i=1;i<n;i++){
            int x,y,z;
            scanf("%d%d",&x,&y);
            add_edge(x,y);
            is_root[y]=false;
        }
        for(int i=1;i<=n;i++)
            if(is_root[i]==true)
                root=i;
        dfs1(root,0,0);
        dfs2(root,1);
        int x,y;
        scanf("%d%d",&x,&y);
        cout<<LCA(x,y)<<endl;
    }
}



猜你喜欢

转载自blog.csdn.net/Akatsuki__Itachi/article/details/82841223