您的位置:首页 > 其它

LCA 问题 用 Tarjan 离线算法 求解

2016-02-27 15:39 393 查看
题目:http://poj.org/problem?id=1330

思 路: LCA (最近公共祖先问题),目的是求出一棵树中,任意两个节点的最近公共祖先。

例:


则 : 4 和 7 的最近公共祖先 是 4 ;

9 和 1 的最近公共祖先 是 8 ;

3 和 2 的最近公共祖先 是 10 ;

Tarjan 算法基于 DFS + 并查集 。

分析: LCA的离线算法为Tarjan,Tarjan算法的流程如下:

DFS遍历整棵树,节点r在遍历完成之后,退回到r在树中的父节点,然后r在 并查集 中的pre指针会指向这个父节点。即对一个节点来说, 所有通过它被遍历过 的子树,都会在并查集中被 合并到这个节点所在的集合上,并以它作为代表元。

初始化每个节点各是一个集合,每次合并操作都是伴随着遍历中的退后行为进行的。 这样就产生了一个性质,在遍历过程中,一个被遍历过的节点的集合的代表元, 就是从树根到这个节点 的路径上,DFS 退后到了的那个节点。

在遍历离开当前节点之前,假设我们刚刚已经完成访问的节点是v,那么我们看与其一同被询问的另外一个点 u 是否已经被访问过了,若已经被访问过了,那么这个时候最近公共祖先必然是 u 所在集合对应的祖先 c,因为我们对 v 的访问就是从最近公共祖先 c 转过来的,并且在从c的子树 u 转向 v 的时候,我们已经将u的祖先置为了c,同时这个 c 也必然是 v 的祖先,那么c必然是u、v的最近公共祖先。

总的时间复杂度是O(n+q)的,因为DFS是O(n)的,对于询问的处理是O(q)。

#include<iostream>
#include<vector>

using namespace std;
const int maxN=10001;

vector<int> vec[maxN];
int    pre[maxN];     //并查集的核心表
bool  root[maxN];     // 看哪一个节点是根节点,有且只有一个 节点的值为 true
bool visit[maxN];     //存放节点是否被访问的表

int u,v;

void Init(int n){     //初始化
for( int i = 1;i <= n;i++ ){
pre[i] = i; //初始化每个节点都是一个集合
root[i] = true;//初始化每个节点都是根
visit[i] = false;//都未被访问
vec[i].clear();//清除上一组数据
}
}

int find(int n){     //查找这个节点属于哪一个集合
if( pre
== n ) //如果相等,说明这个节点就是DFS当前回退到的那个节点,它还没有被更高的节点合并
return n;
else              //不相等,则说明 这个节点已经被 它的父节点的集合 合并了。
return pre
=find( pre
);//需要递归查询当前它属于哪一个集合。

}

void Union(int a,int b){//合并集合。集合的大小,从下向上是逐渐增大的。
int x = find(a);
int y = find(b);

if( x == y )
return ;
pre[y] = x;

}

void LCA( int r){
for( int i = 0;i < vec[r].size();i++ ){
LCA( vec[r][i] );
Union( r,vec[r][i] );
}
//遍历完当前节点 r 的每一个子树之后,才会返回到 r,即后序遍历 。
//这个性质就是我们使用Tarjan算法解决最近公共祖先问题的核心思想。
visit[r] = true;

if( r == u && visit[v] == true){
cout << "LCA of " << u << " and " << v
<< " is :" << find(pre[v]) << endl;
return ;
}
if( r == v && visit[u] == true){
cout << "LCA of " << u << " and " << v
<< " is :" << find(pre[u]) << endl;
return ;
}

}

int main(void){
int T,n;
int a,b;

cin >> T;
while(T--){
cin >> n;
Init(n);
for( int i = 1;i < n;i++ ){
cin >> a >> b;
vec[a].push_back(b);
root[b] = false;
}
cin >> u >> v;

for( int i = 1;i <= n;i++ ){
if( root[i] == true ){
cout << "root :" << i <<endl;
LCA(i);
break;
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: