LCA离线算法-Tarjan算法

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 

猜你喜欢

转载自blog.csdn.net/qq_31741481/article/details/84929005