Tarjan算法的关键就在于ancestor数组。该数组用来存储每个节点的祖先。注意,我们在tarjan算法中用到了并查集,但是并查集中每个集合的最终father并不是所谓的祖先,两个不是同一个东西!并查集中的father是一个叶子节点,为什么这么做呢?因为我们在更新中间节点的祖先时,采用ancestor[find(x)]=x操作,如果中间节点的祖先是它的叶子节点,那么find(x)操作会一直向下把直到叶子节点(它是最终father)上路径上所有节点的祖先都顺道成功更新了了!某个节点x的子树上所有节点的ancestor不正是x吗?只是因为x在DFS中,是后遍历并且并入并查集的节点,这个巧妙地操作就完成了x的子树上所有祖先ancestor的更新。
CODE:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1001;
vector<int> tree[MAXN];
int indegree[MAXN], ancestor[MAXN];
int nvertex, root;
int Father[MAXN], rnk[MAXN];
bool visited[MAXN];
int nquery;
vector<int> query[MAXN];
void init()
{
for (int i = 0; i < nvertex; ++i) {
tree[i].clear();
indegree[i] = 0;
ancestor[i] = i;
Father[i] = i;
rnk[i] = 0;
visited[i] = false;
query[i].clear();
}
}
int FindRoot(int x)
{
return Father[x] == x ? x : Father[x] = FindRoot(Father[x]);
}
void Umerge(int x, int y)
{
x = FindRoot(x), y = FindRoot(y);
if (x != y)
{
Father[x] = y;
}
}
void Tarjan(int x)
{
for (int i = 0; i < tree[x].size(); ++i) {
Tarjan(tree[x][i]);
Umerge(x, tree[x][i]);
ancestor[FindRoot(x)] = x;
}
visited[x] = true;
for (int i = 0; i < query[x].size(); ++i) {
if (visited[query[x][i]])
printf("the LCA for %d and %d is %d\n", x, query[x][i], ancestor[FindRoot(query[x][i])]);
}
}
int main()
{
scanf("%d", &nvertex);
init();
int x, y;
for (int i = 1; i < nvertex; ++i) {
scanf("%d%d", &x, &y);
tree[x].push_back(y); //x->y
indegree[y]++;
}
scanf("%d", &nquery);
for (int i = 0; i < nquery; ++i) {
scanf("%d%d", &x, &y);
query[x].push_back(y);
query[y].push_back(x);
}
for (int i = 0; i < nvertex; ++i)
if (indegree[i] == 0) { root = i; break; }
Tarjan(root);
return 0;
}
转自:https://blog.csdn.net/FreeeLinux/article/details/54974044