LCA(Least Common Ancestors)是指树结构中两个结点的最低的公共祖先。而LCA算法则是用于求两个结点的LCA。当只需要求一对结点的LCA时,我们很容易可以利用递归算法在O(n)的时间复杂度内实现,其中n为树中的结点数目。但是有时候我们会要求计算多组结点对各自的LCA,这样总的时间复杂度将会到达O(nq),其中q为问题总数,这往往是无法接受的。

  LCA离线算法用于计算一组预先给出的结点对各自的LCA(即我们允许在拥有所有结点对信息之后再进行解答)。Tarjan算法即是一种LCA离线算法。我们需要为结点维护一下属性:实现并查集所需要的属性p和rank,children用于保存所有直接孩子结点,ancestor用于记录结点的祖先,visited用于记录结点是否已经访问过,questionList用于记录与node相关的LCA问题。

 tarjan(node)
for child in node.children
tarjan(child)
union(child, node)
node.findSet().ancestor = node
node.visited = true
for question in node.questionList
other = another node specified by question //将other设定为question中涉及到的另外一个问题
if(other.visited)
question.answer = other.findSet().ancestor

  上面就是LCA的所有部分了。其中union和findSet分别用于合并并查集以及查找结点所在并查集的代表结点。要解决所有的LCA问题,只需要用树中的根结点调用tarjan函数即可。

  先说明时间复杂度,由于并查集的所有操作摊还代价都可以视作为O(1),因此tarjan函数的2~6行实际上就是一个普通的深度优先搜索而已,其时间复杂度为O(n)。而第7~10行每次循环都会扫描一个问题,且每个问题只涉及两个结点,故最多只会被扫描两次,因此只会被调用O(q)次,故tarjan函数总共花费的时间复杂度为O(n+q),这无疑是优秀的时间复杂度。

  再说明算法的正确性:只需要说明每个问题都被正确求解了。从两方面说明,1.每个问题的answer属性都被设置过.2.每次对问题的answer属性进行设置时,其值总是正确的。

  由于每个问题都会被扫描两次,在第一次扫描结束后,之后会执行第6行将结点设置为已访问。而在第二次扫描时,发现另外一个结点已经被访问过了,因此会执行第10行代码,对answer属性进行设置。因此我们保证每个问题都会被解答,且以第二次扫描时的答案为最终答案。因此方面1被成功证明。

  假设问题Q问的是结点u和v的LCA,并且假设其LCA为a。分三种情况讨论,1是u=v,2是u!=v=a,3是u、v、a三者均不同。当情况1发生时,u在对所有孩子递归完后,扫描涉及自身的问题时,会将问题解决两次,而每次都将答案设置为自身,故这种情况下赋值是正确的。当第2种情况发生时,我们在v对孩子进行递归完毕后,会将所有孩子都合并到v所在的集合中,并将v所在集合的代表结点的祖先设置为v。而之后扫描问题时会遇到Q,此时由于u已经被访问过了,会将答案设置为v,此时答案是正确的(并且对该问题的第二次扫描已经完成,问题不会被重复赋值)。对于情况3,不妨设u在v之前被访问。由于a是u和v的LCA,因此a的任意子结点都不可能是u和v的LCA,即u和v挂在a的两个不同的子结点下。故当我们访问完u,并回溯到a时,会将u加入到a所在集合,并将a所在集合的祖先设置为a。而之后搜索到v后,v在扫描到Q时,会将Q的answer值设置为u.findSet().ancestor,此时u依旧处于a所在集合中,而a所在集合的祖先始终为a(因为第4~5行代码只会将以当前结点为根的子树中的结点加入到自身所代表的集合中,而由于a的流程尚未走完),因此answer为a,答案正确。  

Tarjan的LCA离线算法的更多相关文章

  1. LCA离线算法Tarjan详解

    离线算法也就是需要先把所有查询给保存下来,最后一次输出结果. 离线算法是基于并查集实现的,首先就是初始化P[i] = i. 接下来对于每个点进行dfs: ①首先判断是否有与该点有关的查询,如果当前该点 ...

  2. poj1330+hdu2586 LCA离线算法

    整整花了一天学习了LCA,tarjan的离线算法,就切了2个题. 第一题,给一棵树,一次查询,求LCA.2DFS+并查集,利用深度优先的特点,回溯的时候U和U的子孙的LCA是U,U和U的兄弟结点的子孙 ...

  3. Closest Common Ancestors---poj1470(LCA+离线算法)

    题目链接:http://poj.org/problem?id=1470 题意是给出一颗树,q个查询,每个查询都是求出u和v的LCA:    以下是寻找LCA的预处理过程: void LCA(u){ f ...

  4. HDU 2874 Connections between cities(LCA离线算法实现)

    http://acm.hdu.edu.cn/showproblem.php?pid=2874 题意: 求两个城市之间的距离. 思路: LCA题,注意原图可能不连通. 如果不了解离线算法的话,可以看我之 ...

  5. 距离LCA离线算法Tarjan + dfs + 并查集

    距离B - Distance in the Tree 还是普通的LCA但是要求的是两个节点之间的距离,学到了一些 一开始我想用带权并查集进行优化,但是LCA合并的过程晚于离线计算的过程,所以路径长度会 ...

  6. LCA离线算法Tarjan的模板

    hdu 2586:题意:输入n个点的n-1条边的树,m组询问任意点 a b之间的最短距离 思路:LCA中的Tarjan算法,RMQ还不会.. #include <stdio.h> #inc ...

  7. HDU 2874 LCA离线算法 tarjan算法

    给出N个点,M条边.Q次询问 Q次询问每两点之间的最短距离 典型LCA 问题   Marjan算法解 #include "stdio.h" #include "strin ...

  8. POJ1986 DistanceQueries 最近公共祖先LCA 离线算法Tarjan

    这道题与之前那两道模板题不同的是,路径有了权值,而且边是双向的,root已经给出来了,就是1,(这个地方如果还按之前那样来计算入度是会出错的.数据里会出现多个root...数据地址可以在poj的dis ...

  9. 1128 - Greatest Parent---LightOj(LCA+离线算法)

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1128 给你一颗树,树的每个节点都有一个权值,树根是节点0,权值为1,树中每个节点的权值 ...

随机推荐

  1. <tf-idf + 余弦相似度> 计算文章的相似度

    背景知识: (1)tf-idf 按照词TF-IDF值来衡量该词在该文档中的重要性的指导思想:如果某个词比较少见,但是它在这篇文章中多次出现,那么它很可能就反映了这篇文章的特性,正是我们所需要的关键词. ...

  2. 在ubuntu16.04上安装php7 mysql5.7 nginx1.10并支持http2

    安装nginx 首先更新软件包 并且安装nginx sudo apt-get update sudo apt-get install nginx 开放防火墙配置 sudo ufw allow 'Ngi ...

  3. Oracle回收站 使用

    查询回收站 SELECT * FROM RECYCLEBIN; SELECT * FROM USER_RECYCLEBIN; --USER_RECYCLEBING与RECYCLEBIN是同义词,字段完 ...

  4. 5,基于关系和超链接的 API

    Tutorial 5: Relationships & Hyperlinked APIs At the moment relationships within our API are repr ...

  5. z变换的性质

    z变换的许多重要性质在数字信号处理中常常要用到. 序列 z变换 收敛域 1)x(n) X(z) Rx-< |z| <Rx+ 2)y(n) Y(z) Ry-< |z| <Ry+ ...

  6. PowerDesigner导出word表结构

    一.wordTemplate.rtp下载 首先下载wordTemplate.rtp,将该文件放在一下路径下 C:\Program Files (x86)\Sybase\PowerDesigner 16 ...

  7. RelativeLayout里的gravity不能居中的解决方法

    最近在遇到RelativeLayout里的gravity属性给它复制center_horizontal或者center都不能居中它的子组件,后来找到了替代方法,只要在它的每个子组件里加上android ...

  8. 找到div下的第一个ul

    $("div#div的id ul li a")//选择的是div下 ul下所有li下的所有a标签 $("div#div的id").children(" ...

  9. 让maven生成可运行jar包

    平时项目大多用到的是war包,今天实现了一个简单功能,无需部署到web服务器上,只需本地跑java代码即可,因此只要生成一个jar包.那么怎么让maven项目打成一个可以使用java命令跑的jar包呢 ...

  10. Instantiate实例化的注意事项

    _obj= Resources.Load("xxx") as GameObject;Instantiate(_obj); 这里的_obj对象和 _obj= Instantiate( ...