最近公共祖先LCA详细解法

最近公共祖先(LCA)是树上倍增的一种典型例题。

问题描述:给定一棵具有n个节点的树,询问两个节点x,y的最近的公共祖先。

分析:首先我们想到的是暴力算法,当然这个算法妥妥的会TLE掉,所以我们采用倍增优化的方法来进行实现,也就是我们今天介绍的重点。

  所谓倍增,就是按2的倍数来增大,按1,2,4,8,16......来跳。不过我们不能按正着的顺序跳,需要按......16,8,4,2,1的顺序来跳。因为如果从小的开始跳,有可能出现“悔棋”的现象(5 不等于 1 + 2 +4,这样就必须要往回跳两个)。

  要想实现这个算法,我们就必须要记录各个点的深度和他们2的i次方的祖先,用一个二维数组可以完美解决~

  代码如下:

void dfs(int f,int fath) //f表示当前节点,fath表示它的父亲节点
{
    depth[f]=depth[fath]+1;
    fa[f][0]=fath;
    for(int i=1;(1<<i)<=depth[f];i++)
        fa[f][i]=fa[fa[f][i-1]][i-1]; //这个转移可以说是算法的核心之一
                                //意思是f的2^i祖先等于f的2^(i-1)祖先的2^(i-1)祖先
                                //2^i=2^(i-1)+2^(i-1)
    for(int i=head[f];i;i=e[i].nex)
        if(e[i].t!=fath)
           dfs(e[i].t,f);
}

  接下来我们就可以取LCA了

int lca(int x,int y)
{
if(depth[x]<depth[y]) //用数学语言来说就是:不妨设x的深度 >= y的深度
  swap(x,y);
while(depth[x]>depth[y])
  x=fa[x][lg[depth[x]-depth[y]]-1]; //先跳到同一深度
if(x==y)  //如果x是y的祖先,那他们的LCA肯定就是x了
  return x;
for(int k=lg[depth[x]]-1;k>=0;k--) //不断向上跳(lg就是之前说的常数优化)
  if(fa[x][k]!=fa[y][k])  //因为我们要跳到它们LCA的下面一层,所以它们肯定不相等,如果不相等就跳过去。
    x=fa[x][k], y=fa[y][k];
return fa[x][0];  //返回父节点
}

  总体来说就是这样了,不知道各位大佬有没有看懂呢?

原文地址:https://www.cnblogs.com/byfrc/p/9892870.html