[算法] LCA 最近公共祖先 (Tarjan)
2017-09-27 19:54
381 查看
今天让我们来看看LCA算法中的一个离线算法Tarjan
首先,我们必须先明确什么是LCA,也就是最近公共祖先。对于有根树上的两个结点u、v,最近公共祖先LCA(u,v)表示一个结点x,满足x是u、v的公共祖先且x的深度尽可能大,也就是离u、v最近的公共祖先(这不是废话?)。
那么什么是LCA的Tarjan算法呢?
利用并查集优越的时空复杂度,我们可以实现LCA问题的O(n+Q)算法,这里Q表示询问的次数。Tarjan算法基于深度优先搜索的框架,对于新搜索到的一个结点,首先创建由这个结点构成的集合,再对当前结点的每一个子树进行搜索,每搜索完一棵子树,则可确定子树内的LCA询问都已解决。其他的LCA询问的结果必然在这个子树之外,这时把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先。之后继续搜索下一棵子树,直到当前结点的所有子树搜索完。这时把当前结点也设为已被检查过的,同时可以处理有关当前结点的LCA询问,如果有一个从当前结点到结点v的询问,且v已被检查过,则由于 进行的是深度优先搜索,当前结点与v的最近公共祖先一定还没有被检查,而这个最近公共祖先的包涵v的子树一定已经搜索过了,那么这个最近公共祖先一定是v 所在集合的祖先。
其实我认为这个算法就是将当前子树的情况全部处理完,然后在将子树合并即可得到最终答案
Tarjan算法优点:令人眼红时间复杂度O(n+Q)
Tarjan算法缺点:如果题目要求强制在线操作(比如说查询一个修改一个什么值),而你又只会Tarjan算法的话,那么你就BOOM~了
基本思路:
下面详细介绍一下Tarjan算法的基本思路:
1.任选一个点为根节点,将当前节点son设置为根节点
2.遍历该点son所有子节点to
3.若是to有子节点,递归将to作为当前节点u,进行操作2,退出时将to的父亲标记为当前节点son
4.将当前节点son标记为访问过
5.寻找与当前节点son有询问关系的点x
6.若x已经被访问过,则可以确认son和x的最近公共祖先为find(x)。find操作等价于并查集find操作,可以加上路径压缩。
伪代码:
多说无益,让我们来看图(其实上面都是废话):
这是一棵有5个节点的树
举以下经典例子:
2-3
1-3
模拟过程:
1.当前节点:son=0,搜索儿子
2.当前节点:son=1,搜索儿子
3.当前节点:son=3,搜索儿子,当前节点没有儿子,保存父亲 f[3]=1,标记访问 vis[3]=true,寻找与当前节点3有询问关系的结点(1,2),vis[1]=false,vis[2]=false,不操作,结束当前层操作,回到上一层
4.当前节点:son=1,搜索儿子
5.当前节点:son=4,搜索儿子,当前节点没有儿子,保存父亲 f[4]=1,标记访问 vis[4]=true,寻找与当前节点1有询问关系的结点(无节点),结束当前层操作,回到上一层
6.当前节点:son=1,搜索儿子,当前节点没有儿子,保存父亲 f[1]=0,标记访问 vis[1]=true,寻找与当前节点1有询问关系的结点(3),vis[3]=true,ans=find(3)=1,结束当前层操作,回到上一层
7.当前节点:son=0,搜索儿子
8.当前节点:son=2,搜索儿子,当前节点没有儿子,保存父亲 f[2]=0,标记访问 vis[2]=true,寻找与当前节点2有询问关系的结点(3),vis[3]=true,ans=find(3)=0,结束当前层操作,回到上一层
9.当前节点:son=0,搜索儿子,当前节点没有儿子,保存父亲 f[0]=0,标记访问 vis[2]=true,寻找与当前节点2有询问关系的结点(无节点),结束当前层操作,回到上一层
10.退出
结果:0 1
CodeVS 2370 小机房的树 传送门 题解
有任何问题的可以在博客下留言或者私信我, 我看到就将会回复
From:Chlience
首先,我们必须先明确什么是LCA,也就是最近公共祖先。对于有根树上的两个结点u、v,最近公共祖先LCA(u,v)表示一个结点x,满足x是u、v的公共祖先且x的深度尽可能大,也就是离u、v最近的公共祖先(这不是废话?)。
那么什么是LCA的Tarjan算法呢?
利用并查集优越的时空复杂度,我们可以实现LCA问题的O(n+Q)算法,这里Q表示询问的次数。Tarjan算法基于深度优先搜索的框架,对于新搜索到的一个结点,首先创建由这个结点构成的集合,再对当前结点的每一个子树进行搜索,每搜索完一棵子树,则可确定子树内的LCA询问都已解决。其他的LCA询问的结果必然在这个子树之外,这时把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先。之后继续搜索下一棵子树,直到当前结点的所有子树搜索完。这时把当前结点也设为已被检查过的,同时可以处理有关当前结点的LCA询问,如果有一个从当前结点到结点v的询问,且v已被检查过,则由于 进行的是深度优先搜索,当前结点与v的最近公共祖先一定还没有被检查,而这个最近公共祖先的包涵v的子树一定已经搜索过了,那么这个最近公共祖先一定是v 所在集合的祖先。
其实我认为这个算法就是将当前子树的情况全部处理完,然后在将子树合并即可得到最终答案
Tarjan算法优点:令人眼红时间复杂度O(n+Q)
Tarjan算法缺点:如果题目要求强制在线操作(比如说查询一个修改一个什么值),而你又只会Tarjan算法的话,那么你就BOOM~了
基本思路:
下面详细介绍一下Tarjan算法的基本思路:
1.任选一个点为根节点,将当前节点son设置为根节点
2.遍历该点son所有子节点to
3.若是to有子节点,递归将to作为当前节点u,进行操作2,退出时将to的父亲标记为当前节点son
4.将当前节点son标记为访问过
5.寻找与当前节点son有询问关系的点x
6.若x已经被访问过,则可以确认son和x的最近公共祖先为find(x)。find操作等价于并查集find操作,可以加上路径压缩。
int find(int x) { //和并查集的操作一致(顺便路径压缩) if(x==f[x]) return x; else return f[x]=find(f[x]); }
伪代码:
void Tarjan(int fa,int son) { for(int i=当前节点son的儿子) { Tarjan(son,i); f[i]=son; } vis[son]=true; for(int i=与当前节点son有询问关系的点) if(vis[to]) ans=find(i); return ; }
多说无益,让我们来看图(其实上面都是废话):
这是一棵有5个节点的树
举以下经典例子:
2-3
1-3
模拟过程:
1.当前节点:son=0,搜索儿子
2.当前节点:son=1,搜索儿子
3.当前节点:son=3,搜索儿子,当前节点没有儿子,保存父亲 f[3]=1,标记访问 vis[3]=true,寻找与当前节点3有询问关系的结点(1,2),vis[1]=false,vis[2]=false,不操作,结束当前层操作,回到上一层
4.当前节点:son=1,搜索儿子
5.当前节点:son=4,搜索儿子,当前节点没有儿子,保存父亲 f[4]=1,标记访问 vis[4]=true,寻找与当前节点1有询问关系的结点(无节点),结束当前层操作,回到上一层
6.当前节点:son=1,搜索儿子,当前节点没有儿子,保存父亲 f[1]=0,标记访问 vis[1]=true,寻找与当前节点1有询问关系的结点(3),vis[3]=true,ans=find(3)=1,结束当前层操作,回到上一层
7.当前节点:son=0,搜索儿子
8.当前节点:son=2,搜索儿子,当前节点没有儿子,保存父亲 f[2]=0,标记访问 vis[2]=true,寻找与当前节点2有询问关系的结点(3),vis[3]=true,ans=find(3)=0,结束当前层操作,回到上一层
9.当前节点:son=0,搜索儿子,当前节点没有儿子,保存父亲 f[0]=0,标记访问 vis[2]=true,寻找与当前节点2有询问关系的结点(无节点),结束当前层操作,回到上一层
10.退出
结果:0 1
到这里我们的模拟就结束了!
你学懂了么?
是不是对Tarjan算法有了更深的认识了呢?
题库-题解(未完待续):CodeVS 2370 小机房的树 传送门 题解
有任何问题的可以在博客下留言或者私信我, 我看到就将会回复
From:Chlience
相关文章推荐
- hdu2586 How far away? Tarjan(离线)算法求最近公共祖先LCA 待补完
- Tarjan算法求LCA(最近公共祖先)
- tarjan离线算法-LCA最近公共祖先算法模板(详细)
- LCA最近公共祖先的离线算法(Tarjan)和在线算法(ST)
- c++最近公共祖先LCA(倍增算法和tarjan)
- POJ1330 Nearest Common Ancestors【最近公共祖先】【Tarjan-LCA算法】
- POJ1986 Distance Queries【最近公共祖先】【Tarjan-LCA算法】
- HDU2586 How far away ?【最近公共祖先】【Tarjan-LCA算法】
- 最近公共祖先(LCA)算法实现过程 【Tarjan离线+倍增在线+RMQ】
- LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现
- LCA 最近公共祖先——Tarjan(离线)算法的基本思路及其算法实现
- LCA 最近公共祖先-Tarjan(离线)算法的基本思路及其算法实现
- 最近公共祖先LCA(Tarjan与DFS--ST倍增)
- LCA最近公共祖先算法
- 最近公共祖先(LCA):tarjan与倍增
- POJ1330 Nearest Common Ancestors(在线倍增,离线Tarjan,最近公共祖先LCA)
- LCA 最近公共祖先 tarjan离线 总结 结合3个例题
- 最近公共祖先LCA(Tarjan算法)的思考和算法实现
- 谈谈最近公共祖先(LCA)——杨子曰算法
- 二叉树的LCA(最近公共祖先)算法