对于一棵树,求两个节点的最近公共祖先(LCA)。
如下图:(以下数字代表对应编号的节点)
11 和 66 的 LCA 是 88 。
1111 和 11 的 LCA 是 88 。
1111 和 1515 的 LCA 是 44 。
1414 和 1313 的 LCA 是 11 。
以洛谷P3379模板题为例
方法一:用倍增来在线求 LCA ,时间和空间复杂度分别是 O((n+q)logn)O((n+q)logn) 和 O(nlogn)O(nlogn) 。
对于这个算法,我们从最暴力的算法开始:
①如果 aa 和 bb 深度不同,先把深度调浅,使他变得和浅的那个一样
②现在已经保证了 aa 和 bb 的深度一样,所以我们只要把两个一起一步一步往上移动,直到他们到达同一个节点,也就是他们的最近公共祖先了。
#include<iostream> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int maxn=5e5+5; int lg[maxn];//log 2n向下取整 const int maxbit=20; vector<int>G[maxn]; int depth[maxn];//记录每个节点的深度 int father[maxn][maxbit];//father[i][j]指的是i节点往上2^j的节点 void dfs(int nowp,int fa) { depth[nowp]=depth[fa]+1;//当前节点深度是父节点深度+1 father[nowp][0]=fa; for(int j=1;j<=lg[depth[nowp]];j++)//倍增求father father[nowp][j]=father[father[nowp][j-1]][j-1]; for(int i=0;i<G[nowp].size();i++) { if(G[nowp][i]!=fa) dfs(G[nowp][i],nowp); } } int LCA(int u,int v) { if(depth[u]<depth[v])//维护u的深度最大 swap(u,v); while(depth[u]!=depth[v]) u=father[u][lg[depth[u]-depth[v]]];//使两个节点在同一高度; if(u==v) return u; for(int j=lg[depth[u]];j>=0;--j) { if(father[u][j]!=father[v][j]) { u=father[u][j]; v=father[v][j]; } } //得到最近公共祖先下一位; return father[u][0]; } int main() { lg[0]=-1; for(int i=1;i<maxn;i++) lg[i]=lg[i>>1]+1; //dfs(s,0);//假设根结点的父节点为0; int n,m,s;int x,y; scanf("%d%d%d",&n,&m,&s); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); G[x].push_back(y); G[y].push_back(x); } dfs(s,0);//假设根结点的父节点为0; while(m--) { scanf("%d%d",&x,&y); printf("%d\n",LCA(x,y)); } return 0; }