1. 程式人生 > >LCA(最近公共祖先)算法

LCA(最近公共祖先)算法

nlogn i++ 百度百科 detail 如果 當前 根節點 節點 strong

參考博客:https://blog.csdn.net/my_sunshine26/article/details/72717112

首先看一下定義,來自於百度百科

  LCA(Lowest Common Ancestors),即最近公共祖先,是指在有根樹中,找出某兩個結點u和v最近的公共祖先。

技術分享圖片

註意:這裏某個節點本身也是它的祖先節點。

求最近公共祖先的算法:

1、暴力:每次查詢的時間復雜度為O(N)

2、Tarjan(離線)算法:在一次遍歷中把所有查詢解決,預處理時間復雜度O(nlogn),每次查詢時間復雜度O(1),總時間復雜度是O(nlogn+q)

3、倍增算法:利用二分兩個節點同時往上走,直到相遇,預處理時間復雜度O(nlogn),每次查詢時間復雜度O(logn)


Tarjan(離線)算法

一.Tarjan算法大致實現過程

  1. 先選擇一個節點u為根節點,從根節點開始搜索。(標記u已訪問過)
  2. 遍歷該點u的所有兒子節點v,並標記v已訪問過。
  3. 若v還有兒子節點,對v重復ii操作,否則進入下一操作。
  4. 把v合並到u上(並查集)。
  5. 把當前的點設為u,遍歷與u有詢問關系的節點v。
  6. 如果v在之前已經被訪問過,那麽u和v的最近公共祖先就是v通過並查集合並後的父親節點(註意是合並後),即當前的find(v)。

二.Tarjan算法的偽代碼

  
 1 Tarjan(u)           //根節點u
 2 {
3 for each(u,v) 4 { 5 Tarjan(v); //v還有兒子節點 6 join(u,v); //把v合並到u上 7 vis[v]=1; //訪問標記 8 } 9 for each(u,v) //遍歷與u有詢問關系的節點v 10 { 11 if(vis[v]) 12 { 13 ans=find(v); 14 } 15 } 16 }

三.Tarjan算法的代碼

 1 void
Tarjan(int u) 2 { 3 vis[u]=1; 4 for(int i=0;i<mp[u].size();i++) 5 { 6 int v=mp[u][i]; 7 if(vis[v]==0) 8 { 9 Tarjan(v); 10 join(u,v); 11 } 12 } 13 for(int i=0;i<mp2[u].size();i++)//利用mp2集合來存儲查詢關系 14 { 15 int v=mp2[u][i]; 16 if(vis[v]==1) 17 { 18 lca[u][v]=find(v); 19 } 20 } 21 }

倍增算法

一、算法鋪墊

首先讓u和v中較深的一個往上走|depth(u)-depth(v)|步,然後再一起一步步往上走,直到走到同一個節點,就可以在O(depth(u)+depth(v))的時間內求出LCA。

關鍵在於如何優化向上查找的過程

二.倍增算法的實現過程

分析剛才的算法,兩個節點到達同一節點後,不論怎麽向上走,達到的顯然還是同一節點。利用這一點,我們就能夠利用二分搜索求出到達最近公共祖先的最小步數了。

首先我們要進行預處理。對於任意的節點,可以通過fa2[v]=fa[fa[v]]得到其向上走2步到達的頂點,再利用這個信息,又可以通過fa4[v]=fa2[fa2[v]]得到其向上走4步所到的頂點。以此類推,我們可以得到其向上走2^k步所到的頂點fa[v][k],預處理的時間點復雜度為O(nlogn)。

 1 void init()
 2 {
 3     lg[1]=0;
 4     for(int i=2;i<=n;i++)
 5       lg[i]=lg[i-1]+(1<<(lg[i-1]+1)==i);//用來求log2(n)
 6 }
 7 void dfs(int f,int fath)
 8 {
 9     deepth[f]=deepth[fath]+1;
10     fa[f][0]=fath;
11     for(int i=1;(1<<i)<=deepth[f];i++)
12       fa[f][i]=fa[fa[f][i-1]][i-1];
13     for(int i=0;i<mp[f].size();i++)
14       if(mp[f][i]!=fath)
15         dfs(mp[f][i],f);
16 }
17 int lca(int x,int y)
18 {
19     if(deepth[x]<deepth[y])
20       swap(x,y);
21     while(deepth[x]>deepth[y])
22       x=fa[x][lg[deepth[x]-deepth[y]]];
23     if(x==y)
24       return x;
25     for(ll k=lg[deepth[x]];k>=0;k--)
26       if(fa[x][k]!=fa[y][k])
27         x=fa[x][k], y=fa[y][k];
28     return fa[x][0];
29 }

LCA(最近公共祖先)算法