LCA(最近公共祖先)算法
阿新 • • 發佈:2018-10-05
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算法大致實現過程
- 先選擇一個節點u為根節點,從根節點開始搜索。(標記u已訪問過)
- 遍歷該點u的所有兒子節點v,並標記v已訪問過。
- 若v還有兒子節點,對v重復ii操作,否則進入下一操作。
- 把v合並到u上(並查集)。
- 把當前的點設為u,遍歷與u有詢問關系的節點v。
- 如果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 voidTarjan(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(最近公共祖先)算法