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号
浙公网安备 33010602011771号