给一棵树,问两个节点的最近公共祖先是什么。
首先要说明的是,tarjan算法是离线的,它一次性读入所有的询问,而且不一定按照读入顺序来处理,但这也是算法的精妙之处;这个算法基于dfs和并查集。
的根...的根)
存储询问也很特别,例如询问a,b,算法把a,b b,a都存下来,保证完整性
接下来我就具体的一棵树来讲解:
的祖先....的祖先),所以当从5回溯到2的时候,我们就要把5和2合并起来,然后把5的祖先修改成2,然后再去递归6,其他过程也就类似了。
附:POJ1330代码:点击打开链接
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=10005;
vector<int>tree[maxn],que[maxn];//树和询问
bool vis[maxn];
int father[maxn];
int ancestor[maxn];
int rank[maxn];
int in[maxn];//入度,找根
int n;
void init()
{
for (int i=1;i<=n;i++)
{
rank[i]=1;
father[i]=i;
in[i]=0;
vis[i]=0;
ancestor[i]=0;
que[i].clear();
tree[i].clear();
}
}
int find(int x)
{
if (x==father[x])
return x;
father[x]=find(father[x]);
return father[x];
}
void Union(int x,int y)
{
int a=find(x);
int b=find(y);
if (a!=b)
{
if (rank[a]<=rank[b]) //合并的时候按深度浅的向深的合并
{
father[a]=b;
rank[b]+=rank[a];
}
else
{
father[b]=a;
rank[a]+=rank[b];
}
}
}
void LCA(int root)
{
ancestor[root]=root;
int size=tree[root].size();
for (int i=0;i<size;i++)
{
LCA(tree[root][i]);
Union(root,tree[root][i]);
//printf("%d %d %d %d\n",root,tree[root][i],find(root),ancestor[find(root)]);
ancestor[find(root)]=root;
}
vis[root]=1;
size=que[root].size();
for (int i=0;i<size;i++)
{
if (vis[que[root][i]])
{
printf("%d\n",ancestor[find(que[root][i])]);
}
}
}
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
int x,y;
scanf("%d",&n);
init();
for (int i=0;i<n-1;i++)
{
scanf("%d%d",&x,&y);
tree[x].push_back(y);
in[y]++;
}
scanf("%d%d",&x,&y);
que[x].push_back(y);
que[y].push_back(x);
for (int i=1;i<=n;i++)
{
if (in[i]==0)
{
LCA(i);
break;
}
}
}
return 0;
}