LCA问题
LCA:在一棵有根树中,在两个节点的公共祖先中,深度最大的一个称为它们的最近公共祖先(LCA)
不难发现暴力求LCA时间复杂度是O(n)的,这里给出两种快速求LCA的方法:
(1)倍增法:类似于ST表,我们用parent[i][k]表示结点i的往上第2^k代祖先结点,在O(nlogn)时间内求出所有parent函数值。查询时,先将两个节点调整至同一高度,再二分k求解,单次查询时间复杂度为O(logn)
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define sz(x) (int)(x.size())
using namespace std;
const int maxn=100000+10,maxlogn=17;
int n,m,k,x,y,parent[maxn][maxlogn+2],depth[maxn];
vector<int> G[maxn];
void DFS(int k,int d)
{
depth[k]=d;
rep(i,0,sz(G[k])-1) DFS(G[k][i],d+1);
}
void init()
{
DFS(1,0); //默认根节点为1,DFS确定结点深度
rep(k,0,maxlogn-1)
rep(i,1,n)
{
if (parent[i][k]==0) parent[i][k+1]=0; else parent[i][k+1]=parent[parent[i][k]][k]; //递推预处理parent数组
}
}
int lca(int u,int v)
{
if (depth[u]<depth[v]) swap(u,v);
dep(i,maxlogn,0)
if ((depth[u]-depth[v])&(1<<i)) u=parent[u][i]; //调整高度
if (u==v) return u; //不要忘记特判!!
dep(i,maxlogn,0)
if (parent[u][i]!=parent[v][i]) u=parent[u[i],v=parent[v][i]; //二分求解
return parent[u][0];
}
int main()
{
scanf("%d",&n);
rep(i,1,n)
{
scanf("%d",&k); //i的父亲为k
parent[i][0]=k;
if (k>0) G[k].push_back(i);
}
init();
scanf("%d",&m);
rep(i,1,m)
{
scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
system("pause");
return 0;
}
(2)基于RMQ的算法:我们利用DFS序,vs[i]表示时刻T正在访问的结点,id[i]表示结点i第一次被访问的时刻,则u与v的LCA等于在vs数组中[min(id[u],id[v]),max(id[u],id[v])]范围内查找一个令depth最小的值,即经典的RMQ问题。
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define sz(x) (int)(x.size())
using namespace std;
const int maxn=100000+10,maxlogn=17+1;
int n,m,k,T,x,y,parent[maxn],vs[maxn*3],depth[maxn],id[maxn],d[maxn][maxlogn];
vector<int> G[maxn];
void DFS(int k,int d)
{
depth[k]=d;
vs[++T]=k; //时间戳
if (!id[k]) id[k]=T;
rep(i,0,sz(G[k])-1) DFS(G[k][i],d+1),vs[++T]=k;
}
void RMQ_init() //RMQ初始化
{
rep(i,1,T) d[i][0]=vs[i];
for (int k=1;(1<<k)<=T;k++)
for (int i=1;i+(1<<k)-1<=T;i++)
if (depth[d[i][k-1]]<depth[d[i+(1<<(k-1))][k-1]]) d[i][k]=d[i][k-1]; else d[i][k]=d[i+(1<<(k-1))][k-1];
}
void init()
{
T=0,memset(id,0,sizeof(id));
DFS(1,0);
RMQ_init();
}
int query(int l,int r) //RMQ查询
{
int k=0;
while ((1<<(k+1))<=(r-l+1)) k++;
if (depth[d[l][k]]<depth[d[r-(1<<k)+1][k]]) return d[l][k]; else return d[r-(1<<k)+1][k];
}
int lca(int u,int v)
{
return query(min(id[u],id[v]),max(id[u],id[v])); //RMQ查找
}
int main()
{
scanf("%d",&n);
rep(i,1,n)
{
scanf("%d",&k); //i的父亲为k
parent[i]=k;
if (k>0) G[k].push_back(i);
}
init(); //初始化
scanf("%d",&m);
rep(i,1,m)
{
scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
system("pause");
return 0;
}

浙公网安备 33010602011771号