最近公共祖先问题 : http://poj.org/problem?id=1330
#include <iostream>
#include <memory.h>
#include <vector>
#include <algorithm>
using namespace std;
/**
理解:
对于节点 a ,左孩子 b ,右孩子 c
1、a与b合并的时候 是当 b 全部遍历完时,遍历不完 不会合并
则此时 b的最近祖先是 b
祖先是运行过程中动态更新的
2、所谓的离线 就是先将所有的query读入,然后处理
*/
//此处并查集采用链式树形存储
const int N = 10010;
int father[N];
int ancestor[N];
int rank[N];
int indegree[N];
std::vector<int > adj[N]; //树的临间表存储
std::vector<int > tmp[N]; //查询邻间表存储
bool visited[N] ; //一个很重要的标志量
void adjinit()
{
int i=0;
for(i=0;i<N;i++)
{
father[i] = i; //设置自己的为自己的父亲
adj[i].clear();
tmp[i].clear();
}
memset(visited,false,sizeof(visited));
fill(rank,rank+N,1); //初始化 每个集合的树的高度
memset(indegree,0,sizeof(indegree)); //初始化每个顶点的入度,
}
//查询 带有路径压缩的查询 查询集合的标志
int findset(int u)
{
if(father[u] == u)
return u;
//带 路径压缩的 查询
father[u] = findset(father[u]);
return father[u];
}
void unionset(int u,int v)
{
u = findset(u);
v = findset(v);
if( u == v) //属于一个集合
return ;
//不属于一个集合,要进行集合的合并
//使用按秩合并方式
if(rank[u] > rank[v])
{
father[v] = u;
}
else
{
father[u] = v;
if(rank[u] == rank[v])
rank[v] += 1; //高度 +1
}
}
void LCA(int u) //u为树根
{
//father[u] = u; //首先make_set
ancestor[ findset(u) ] = u; //设置u所在的集合的祖先为 u
//遍历子树
vector<int>::size_type i;
for(i=0;i<adj[u].size();i++)
{
LCA(adj[u][i]); //深度优先遍历子树
unionset(u,adj[u][i]); //该子树遍历完之后与 祖先u合并
ancestor[findset(u)] = u;//设置u所在的集合的祖先为 u
}
visited[u] = true;
for(i=0;i<tmp[u].size();i++) //遍历查询 查找包含当前节点 的query
{
if( visited[tmp[u][i]] ) //如果query的另一个节点 v 已经标记过 则查找他的祖先 即为最小公共祖先
cout<<ancestor[findset(tmp[u][i])]<<endl;
}
}
int main()
{
int k,n,u,v;
cin>>k; //输入test case
while(k--)
{
cin>>n; //节点的个数
int i=0;
adjinit(); //初始化
//接下来输入 n-1条边 与 1 个query
for(i=0; i<n-1 ; i++)
{
cin>>u>>v;
adj[u].push_back(v); //邻间表存储
indegree[v]++; //入度++
}
//输入query
cin>>u>>v;
tmp[u].push_back(v); //两次输入 是为了便于查询
tmp[v].push_back(u);
for(i=1;i<=n;i++)
if(indegree[i] == 0) //找到树根 并且开始 查找
LCA(i);
}
return 0;
}
借鉴博客: http://blog.csdn.net/xuzengqiang/article/details/7334330