您的位置:首页 > 其它

最近公共祖先LCA Tarjan算法

2014-08-30 10:50 501 查看

     个人觉得十分不错的讲解,弄懂了!

第一次写最近公共祖先问题,用的邻接表指针。

对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点。

       0

       |

       1

     /   \

   2      3

比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点。

在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深搜树,非常好的处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这时候才把u结点放入合并集合中,这样u结点和所有u的子树中的结点的最近公共祖先就是u了,u和还未遍历的所有u的兄弟结点及子树中的最近公共祖先就是u的父亲结点。以此类推。。这样我们在对树深度遍历的时候就很自然的将树中的结点分成若干的集合,两个集合中的所属不同集合的任意一对顶点的公共祖先都是相同的,也就是说这两个集合的最近公共最先只有一个。对于每个集合而言可以用并查集来优化,时间复杂度就大大降低了,为O(n
+ q),n为总结点数,q为询问结点对数。

另外Tarjan解法,是一个离线算法,就是说它必须将所有询问先记录下来,再一次性的求出每个点对的最近公共祖先,只有这样才可以达到降低时间复杂度。另外还有一个在线算法,有待学习,呵呵。。

参考代码:

int find(int n)
{
if(f
==n)
return n;
else
f
=find(f
);
return f
;
}//查找函数,并压缩路径

int Union(int x,int y)
{
int a=find(x);
int b=find(y);
if(a==b)
return 0;
//相等的话,x向y合并
else if(r[a]<=r[b])
{
f[a]=b;
r[b]+=r[a];
}
else
{
f[b]=a;
r[a]+=r[b];
}
return 1;

}//合并函数,如果属于同一分支则返回0,成功合并返回1

void LCA(int u)
{
ancestor[u]=u;
int size = tree[u].size();
for(int i=0;i<size;i++)
{
LCA(tree[u][i]);
Union(u,tree[u][i]);
ancestor[find(u)]=u;
}
visit[u]=1;
size = Qes[u].size();
for(int i=0;i<size;i++)
{
//如果已经访问了问题节点,就可以返回结果了.
if(visit[Qes[u][i]]==1)
{
cout<<ancestor[find(Qes[u][i])]<<endl;
return;
}
}
} 例题应用:http://acm.pku.edu.cn/JudgeOnline/problem?id=1330
代码:(poj 1330)

#include<iostream>
#include<vector>
using namespace std;

const int MAX=10001;
int f[MAX];
int r[MAX];
int indegree[MAX];//保存每个节点的入度
int visit[MAX];
vector<int> tree[MAX],Qes[MAX];
int ancestor[MAX];

void init(int n)
{
for(int i=1;i<=n;i++)
{

r[i]=1;
f[i]=i;
indegree[i]=0;
visit[i]=0;
ancestor[i]=0;
tree[i].clear();
Qes[i].clear();
}

}

int find(int n)
{
if(f
==n)
return n;
else
f
=find(f
);
return f
;
}//查找函数,并压缩路径

int Union(int x,int y)
{
int a=find(x);
int b=find(y);
if(a==b)
return 0;
//相等的话,x向y合并
else if(r[a]<=r[b])
{
f[a]=b;
r[b]+=r[a];
}
else
{
f[b]=a;
r[a]+=r[b];
}
return 1;

}//合并函数,如果属于同一分支则返回0,成功合并返回1

void LCA(int u)
{
ancestor[u]=u;
int size = tree[u].size();
for(int i=0;i<size;i++)
{
LCA(tree[u][i]);
Union(u,tree[u][i]);
ancestor[find(u)]=u;
}
visit[u]=1;
size = Qes[u].size();
for(int i=0;i<size;i++)
{
//如果已经访问了问题节点,就可以返回结果了.
if(visit[Qes[u][i]]==1)
{
cout<<ancestor[find(Qes[u][i])]<<endl;
return;
}
}
}

int main()
{
int cnt;
int n;
cin>>cnt;
while(cnt--)
{
cin>>n;;
init(n);
int s,t;
for(int i=1;i<n;i++)
{
cin>>s>>t;
tree[s].push_back(t);
indegree[t]++;
}
//这里可以输入多组询问
cin>>s>>t;
//相当于询问两次
Qes[s].push_back(t);
Qes[t].push_back(s);
for(int i=1;i<=n;i++)
{
//寻找根节点
if(indegree[i]==0)
{
LCA(i);
break;
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 tarjin LCA