1,最近公用祖先(LCA):
對於有根樹T的兩個結點u、v,最近公用祖先LCA(T,u,v)表示一個結點x,滿足x是u、v的祖先且x的深度儘可能大。
2,LCA問題向RMQ問題的轉化方法:(RMQ返回最值的下標)
對樹進行深度優先遍曆,每當“進入”或回溯到某個結點時,將這個結點的深度存入數組dfsNum最後一位。同時記錄結點i在數組中第一次出現的位置(事實上就是進入結點i時記錄的位置),記做first[i]。如果結點dfsNum[i]的深度記做depth[i],易見,這時求LCA(u,v),就等價於求 E[RMQ(depth,first[u],first[v])],(first[u]<first[v])。
例如:
(深度遍曆)difNum[i]為:1,2,1,3,4,3,5,3,1
first[i]為:0,1,3,4,6
(個點對應的深度)depth[i]為:0,1,0,1,2,1,2,1,0
於是有:
LCA(4,2) = difNum[RMQ(D,first[2],first[4])] = difNum[RMQ(D,1,6)] = difNum[2] = 1
轉化後得到的數列長度為樹的結點數*2-1(每經過一條邊,都會記錄端點,有N-1條邊,所以會回溯N-1次。且每個頂點都會被添加一次,所以長度為2N-1)
3,執行個體代碼:
#include<iostream> #include<vector> #include<cmath> #include<algorithm> using namespace std; const int maxn=20010; struct node //可以添加額外的資訊 { int v;//孩子結點 }; //注意vector在樹問題中的使用 vector<node> tree[maxn]; int dfsnum[maxn]; //記錄遍曆的節點 int depth[maxn]; //記錄節點對應的深度 int first[maxn]; //記錄結點第一次訪問到時的下標 int top; //記錄總的步伐數 void dfs(int m,int f,int dep) //當前節點編號,父節點編號,深度 { dfsnum[top]=m; depth[top]=dep; first[m]=top; top++; for(unsigned i=0;i<tree[m].size();i++) { if(tree[m][i].v==f) continue; dfs(tree[m][i].v,m,dep+1); dfsnum[top]=m; //注:每條邊回溯一次,所以top的值=n+n-1 depth[top]=dep; top++; } } int dp[maxn][18]; void makeRmqIndex(int n,int b[]) //返回最小值對應的下標 { int i,j; for(i=0;i<n;i++) dp[i][0]=i; for(j=1;(1<<j)<=n;j++) for(i=0;i+(1<<j)-1<n;i++) dp[i][j]=b[dp[i][j-1]] < b[dp[i+(1<<(j-1))][j-1]]? dp[i][j-1]:dp[i+(1<<(j-1))][j-1]; } int rmqIndex(int s,int v,int b[]) { int k=(int)(log((v-s+1)*1.0)/log(2.0)); return b[dp[s][k]]<b[dp[v-(1<<k)+1][k]]? dp[s][k]:dp[v-(1<<k)+1][k]; } int lca(int x,int y) { return dfsnum[rmqIndex(first[x],first[y],depth)]; } int main() { int n=5;//頂點數 top=0; //分別存放每條邊的端點 int x[]={1,1,3,3}; int y[]={2,3,4,5}; node temp; for(int i=0;i<n-1;i++) //n-1條邊 { temp.v=y[i]; tree[x[i]].push_back(temp); temp.v=x[i]; tree[y[i]].push_back(temp); } dfs(1,-1,0); //根節點為1 cout<<"總數:"<<top<<endl; makeRmqIndex(top,depth); cout<<"dfsnum:"; for(int i=0;i<top;i++) { cout<<dfsnum[i]<<" "; } cout<<endl; cout<<"depth:"; for(int i=0;i<top;i++) { cout<<depth[i]<<" "; } cout<<endl; cout<<"first:"; for(int i=1;i<=n;i++) { cout<<first[i]<<" "; } cout<<endl; cout<<"lca(4,5):"<<lca(4,5)<<endl; cout<<"lca(2,4):"<<lca(2,4)<<endl; cout<<"lca(1,4):"<<lca(1,4)<<endl; return 0; }