LCA(最近公共祖先)

来源:互联网 发布:nginx配置ip端口访问 编辑:程序博客网 时间:2024/05/17 20:59

定义
对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。现在给定一个根为1的树,求某两个点的最近公共祖先。
思路

预处理出每个点的深度再一层一层返回一直到找到相同点

deep[1]=1;void dfs(int nw,int d){    deep[nw]=d;    for(int i=0;i<G[nw].size();i++){        int to=G[nw][i];        if(!deep[to]){            par[to]=nw;            dfs(to,d+1);        }    }}

再返回

int LCA(int a,int b){    if(deep[a]>deep[b])swap(a,b);    while(deep[a]<deep[b])b=par[b];    while(a!=b){        a=par[a];        b=par[b];    }return a;}

复杂度约为O(n)
但是可以使用倍增优化
以par[i][j] 记录第i个点向上返回2^j的节点
因为2^j==2*2^(j-1)
即得到par[i][j]=ar[par[i][j-1]][j-1];
于是每次dfs出每个点的par[i][0]

void dfs(int c){    for(int i=0;i<G[c].size();i++){        int t=G[c][i];        if(!d[t]){            d[t]=d[c]+1;            par[t][0]=c;            dfs(t);        }       }   }

再利用动态规划计算出par[i][j]

for(int j=1;j<S;j++)    for(int i=1;i<=n;i++)    par[i][j]=par[par[i][j-1]][j-1];

于是我们可以利用类似快速幂的思想

int up(int a,int s){    for(int i=0;i<S;i++)        if(s&(1<<i))a=par[a][i];    return a;}

于是我们就得到了o(log2n)的算法

int LCA(int a,int b){    if(d[a]>d[b])swap(a,b);    b=up(b,d[b]-d[a]);    if(a!=b){    for(int i=S-1;i>=0;i--)        if(par[a][i]!=par[b][i])        a=par[a][i],b=par[b][i];        //找到最近的不同点     a=par[a][0];    //再上一层即为LCA     }return a;}
3 0
原创粉丝点击