LCA算法
2015-08-06 16:28
127 查看
LCA算法:
LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点。也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们就是要求找到公共的节点中,深度尽量深的点。还可以表示成另一种说法,就是如果把树看成是一个图,这找到这两个点中的最短距离。
LCA算法有在线算法也有离线算法,所谓的在线算法就是实时性的,比方说,给你一个输入,算法就给出一个输出,就像是http请求,请求网页一样。给一个实时的请求,就返回给你一个请求的网页。而离线算法则是要求一次性读入所有的请求,然后在统一得处理。而在处理的过程中不一定是按照请求的输入顺序来处理的。说不定后输入的请求在算法的执行过程中是被先处理的。
本文先介绍一个离线的算法,就做tarjan算法。这个算法是基于并查集和DFS的。Dfs的作用呢,就是递归,一次对树中的每一个节点进行处理。而并查集的作用就是当dfs每访问完(注意,这里是访问完)到一个点的时候,就通过并查集将这个点,和它的子节点链接在一起构成一个集合,也就是将并查集中的pnt值都指向当前节点。这样就把树中的节点分成了若干个的集合,然后就是根据这些集合的情况来对输入数据来进行处理。
比方说当前访问到的节点是u,等u处理完之后呢,ancestor[u]就构成了u的集合中的点与u点的LCA,而ancestor[fa[u]]就构成了,u的兄弟节点及其兄弟子树的集合中点与u的LCA,而ancestor[fa[fa[u]]]就构成了u的父亲节点的兄弟节点及其兄弟子树的集合中的点与u的LCA。然后依次类推,这样就构成了这个LCA的离线算法。
对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点。
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为询问结点对数。
LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点。也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们就是要求找到公共的节点中,深度尽量深的点。还可以表示成另一种说法,就是如果把树看成是一个图,这找到这两个点中的最短距离。
LCA算法有在线算法也有离线算法,所谓的在线算法就是实时性的,比方说,给你一个输入,算法就给出一个输出,就像是http请求,请求网页一样。给一个实时的请求,就返回给你一个请求的网页。而离线算法则是要求一次性读入所有的请求,然后在统一得处理。而在处理的过程中不一定是按照请求的输入顺序来处理的。说不定后输入的请求在算法的执行过程中是被先处理的。
本文先介绍一个离线的算法,就做tarjan算法。这个算法是基于并查集和DFS的。Dfs的作用呢,就是递归,一次对树中的每一个节点进行处理。而并查集的作用就是当dfs每访问完(注意,这里是访问完)到一个点的时候,就通过并查集将这个点,和它的子节点链接在一起构成一个集合,也就是将并查集中的pnt值都指向当前节点。这样就把树中的节点分成了若干个的集合,然后就是根据这些集合的情况来对输入数据来进行处理。
比方说当前访问到的节点是u,等u处理完之后呢,ancestor[u]就构成了u的集合中的点与u点的LCA,而ancestor[fa[u]]就构成了,u的兄弟节点及其兄弟子树的集合中点与u的LCA,而ancestor[fa[fa[u]]]就构成了u的父亲节点的兄弟节点及其兄弟子树的集合中的点与u的LCA。然后依次类推,这样就构成了这个LCA的离线算法。
对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点。
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为询问结点对数。
相关文章推荐
- 开心消消乐
- 测试城市电话号码
- Python 时间格式化
- java使用Redis3--完整模板类
- 【暑假】[实用数据结构]动态连续和查询问题
- 如何保存网页
- js获取GET数据
- Cocos2d-x 3.6 项目实战---贪吃蛇(2)
- Hosts文件的作用
- js获取当前时间
- SQLServer excel和txt的导入导出
- UVa 11059 最大乘积(枚举)
- Android定位SDK
- Drogon——利用 method swizzling 解决 iOS APP 限制 URL 跳转的问题
- iOS 合并带有透明通道的视频-
- 单例模式的两种实现方式
- sql test
- gcc,gdb基础学习1
- Java基础针对自己薄弱环节总结04(面向对象多态)
- tomcat参考文章