1. 程式人生 > >最近公共祖先LCA詳細解法

最近公共祖先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];  //返回父節點
}

  總體來說就是這樣了,不知道各位大佬有沒有看懂呢?