LCA算法:
LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点。也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们就是要求找到公共的节点中,深度尽量深的点。还可以表示成另一种说法,就是如果把树看成是一个图,这找到这两个点中的最短距离。
tarjan算法是离线算法,复杂度为O(n+Q),使用了并查集+dfs的操作。中间的那个并查集操作的作用,只是将已经查找过的节点捆成一个集合然后再指向一个公共的祖先。另外,如果要查询LCA(a,b),必须把(a,b)和(b,a)都加入邻接表。
如poj1330为例
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define MAXN 10001
int n,fa[MAXN];
int rank[MAXN];
int indegree[MAXN];
int vis[MAXN];
vector<int> hash[MAXN],Qes[MAXN];
int ances[MAXN];//祖先
void init(int n)
{
for(int i=0;i<=n;i++)
{
fa[i]=i;
rank[i]=0;
indegree[i]=0;
vis[i]=0;
ances[i]=0;
hash[i].clear();
Qes[i].clear();
}
}
int find(int x)
{
if(x != fa[x])
fa[x]=find(fa[x]);
return fa[x];
}
void unio(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx==fy) return ;
if(rank[fy]<rank[fx])
fa[fy]=fx;
else
{
fa[fx]=fy;
if(rank[fx]==rank[fy])
rank[fy]++;
}
}
void Tarjan(int u)
{
ances[u]=u;
int i,size = hash[u].size();
for(i=0;i<size;i++)
{
Tarjan(hash[u][i]);//递归处理儿子
unio(u,hash[u][i]);//将儿子父亲合并,合并时会将儿子的父亲改为u
ances[find(u)]=u;//此时find(u)仍为u,即
}
vis[u]=1;
//查询
size = Qes[u].size();
for(i=0;i<size;i++)
{
if(vis[Qes[u][i]]==1)//即查询的另一个结点开始已经访问过,当前的u在此回合访问。
{
printf("%d\n",ances[find(Qes[u][i])]);//由于递归,此时还是在u
return;
}
}
}
int main()
{
int t;
int i,j;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
init(n);
int s,d;
for(i=1;i<=n-1;i++)
{
scanf("%d%d",&s,&d);
hash[s].push_back(d);
indegree[d]++;
}
scanf("%d%d",&s,&d);
// if(s==d)//如果需要计数的时候注意
// ans[d]++;
// else
// {
Qes[s].push_back(d);
Qes[d].push_back(s);
// }
for(j=1;j<=n;j++)
{
if(indegree[j]==0)
{
Tarjan(j);
break;
}
}
}
return 0;
}