LCA 最近公共祖先

Tarjan(离线)算法的基本思路及其算法实现

小广告:METO CODE 安溪一中信息学在线评测系统(OJ)

      //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下!

      //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!!

    首先是最近公共祖先的概念(什么是最近公共祖先?):

    在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大公共祖先节点

    换句话说,就是两个点在这棵树上距离最近的公共祖先节点

    所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径。

    有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢?

    答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而LCA还可以将自己视为祖先节点

    举个例子吧,如下图所示最近公共祖先是2最近公共祖先最近公共祖先。 

    这就是最近公共祖先的基本概念了,那么我们该如何去求这个最近公共祖先呢?

    通常初学者都会想到最简单粗暴的一个办法:对于每个询问,遍历所有的点,时间复杂度为O(n*q),很明显,n和q一般不会很小

    常用的求LCA的算法有:Tarjan/DFS+ST/倍增

    后两个算法都是在线算法,也很相似,时间复杂度在O(logn)~O(nlogn)之间,我个人认为较难理解。

    有的题目是可以用线段树来做的,但是其代码量很大,时间复杂度也偏高,在O(n)~O(nlogn)之间,优点在于也是简单粗暴

    这篇博客主要是要介绍一下Tarjan算法(其实是我不会在线...)。

    什么是Tarjan(离线)算法呢?顾名思义,就是在一次遍历中把所有询问一次性解决,所以其时间复杂度是O(n+q)

    Tarjan算法的优点在于相对稳定,时间复杂度也比较居中,也很容易理解。

    下面详细介绍一下Tarjan算法的基本思路:

      1.任选一个点为根节点,从根节点开始。

      2.遍历该点u所有子节点v,并标记这些子节点v已被访问过。

      3.若是v还有子节点,返回2,否则下一步。

      4.合并v到u上。

      5.寻找与当前点u有询问关系的点v。

      6.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。

    遍历的话需要用到dfs来遍历(我相信来看的人都懂吧...),至于合并,最优化的方式就是利用并查集来合并两个节点。

    下面上伪代码:

   1 Tarjan(u)//marge和find为并查集合并函数和查找函数
  2 {
  3 for each(u,v) //访问所有u子节点v
  4 {
   Tarjan(v); //继续往下遍历
   marge(u,v); //合并v到u上
   标记v被访问过;
   }
   for each(u,e) //访问所有和u有询问关系的e
   {
   如果e被访问过;
   u,e的最近公共祖先为find(e);
   }
   }

    个人感觉这样还是有很多人不太理解,所以我打算模拟一遍给大家看。

    建议拿着纸和笔跟着我的描述一起模拟!!

    假设我们有一组数据 9个节点 8条边 联通情况如下:

    1--2,1--3,2--4,2--5,3--6,5--7,5--8,7--9 即下图所示的树

    设我们要查找最近公共祖先的点为9--8,4--6,7--5,5--3;

    设f[]数组为并查集的父亲节点数组,初始化f[i]=i,vis[]数组为是否访问过的数组,初始为0; 

    下面开始模拟过程:

    取1为根节点往下搜索发现有两个儿子2和3;

    先搜2,发现2有两个儿子4和5,先搜索4,发现4没有子节点,则寻找与其有关系的点;

    发现6与4有关系,但是vis[6]=0,即6还没被搜过,所以不操作

    发现没有和4有询问关系的点了,返回此前一次搜索,更新vis[4]=1

    

    表示4已经被搜完,更新f[4]=2,继续搜5,发现5有两个儿子7和8;

    先搜7,发现7有一个子节点9,搜索9,发现没有子节点,寻找与其有关系的点;

    发现8和9有关系,但是vis[8]=0,即8没被搜到过,所以不操作;

    发现没有和9有询问关系的点了,返回此前一次搜索,更新vis[9]=1

    表示9已经被搜完,更新f[9]=7,发现7没有没被搜过的子节点了,寻找与其有关系的点;

    发现5和7有关系,但是vis[5]=0,所以不操作

    发现没有和7有关系的点了,返回此前一次搜索,更新vis[7]=1

    

    表示7已经被搜完,更新f[7]=5,继续搜8,发现8没有子节点,则寻找与其有关系的点;

    发现9与8有关系,此时vis[9]=1,则他们的最近公共祖先find(9)=5

      (find(9)的顺序为f[9]=7-->f[7]=5-->f[5]=5 return 5;)

    发现没有与8有关系的点了,返回此前一次搜索,更新vis[8]=1

    表示8已经被搜完,更新f[8]=5,发现5没有没搜过的子节点了,寻找与其有关系的点;

    

    发现7和5有关系,此时vis[7]=1,所以他们的最近公共祖先find(7)=5

      (find(7)的顺序为f[7]=5-->f[5]=5 return 5;)

    又发现5和3有关系,但是vis[3]=0,所以不操作,此时5的子节点全部搜完了;

    返回此前一次搜索,更新vis[5]=1,表示5已经被搜完,更新f[5]=2

    发现2没有未被搜完的子节点,寻找与其有关系的点;

    又发现没有和2有关系的点,则此前一次搜索,更新vis[2]=1

    

    表示2已经被搜完,更新f[2]=1,继续搜3,发现3有一个子节点6;

    搜索6,发现6没有子节点,则寻找与6有关系的点,发现4和6有关系;

    此时vis[4]=1,所以它们的最近公共祖先find(4)=1;

      (find(4)的顺序为f[4]=2-->f[2]=2-->f[1]=1 return 1;)

    发现没有与6有关系的点了,返回此前一次搜索,更新vis[6]=1,表示6已经被搜完了;

    

    更新f[6]=3,发现3没有没被搜过的子节点了,则寻找与3有关系的点;

    发现5和3有关系,此时vis[5]=1,则它们的最近公共祖先find(5)=1

      (find(5)的顺序为f[5]=2-->f[2]=1-->f[1]=1 return 1;)

    发现没有和3有关系的点了,返回此前一次搜索,更新vis[3]=1

    

    更新f[3]=1,发现1没有被搜过的子节点也没有有关系的点,此时可以退出整个dfs了。

    经过这次dfs我们得出了所有的答案,有没有觉得很神奇呢?是否对Tarjan算法有更深层次的理解了呢?

    

    如果有什么不懂可以在下面 留言提问 or 发送问题到1136404654@qq.com。

    推荐几道LCA的题目

          CODEVS 2370 小机房的树 传送门

          CODEVS 1036 商务旅行 传送门

          METO CODE 223 拉力赛 传送门

          HDU 2586 How far way? 传送门

          ZOJ 3195 Design the city 传送门

    相应的题解以后可能会上,大家敬请期待吧。

最近公共祖先LCA(Tarjan算法)的思考和算法实现的更多相关文章

  1. 最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

    LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了f ...

  2. 最近公共祖先 LCA Tarjan算法

    来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html 对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个 ...

  3. 最近公共祖先LCA Tarjan 离线算法

    [简介] 解决LCA问题的Tarjan算法利用并查集在一次DFS(深度优先遍历)中完成所有询问.换句话说,要所有询问都读入后才开始计算,所以是一种离线的算法. [原理] 先来看这样一个性质:当两个节点 ...

  4. POJ 1986 Distance Queries (最近公共祖先,tarjan)

    本题目输入格式同1984,这里的数据范围坑死我了!!!1984上的题目说边数m的范围40000,因为双向边,我开了80000+的大小,却RE.后来果断尝试下开了400000的大小,AC.题意:给出n个 ...

  5. Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)

    Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...

  6. [模板] 最近公共祖先/lca

    简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...

  7. POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)

    POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...

  8. 【lhyaaa】最近公共祖先LCA——倍增!!!

    高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...

  9. POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)

    POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...

随机推荐

  1. 用C#代码实现类似QQ窗体的“上、左、右”停靠功能

    大家都知道QQ有一个自动停靠功能,即“上.左.右”,当你把窗体拖到屏幕边缘,然后移开鼠标它会自动缩放,然后只显示一小小点出来,我们仔细观察会发现其实它只露3像素左右的边缘,当你鼠标移上去它又会伸出来, ...

  2. Java设计模式之《观察者模式》及应用场景

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6513651.html 观察者模式,又可以称之为发布-订阅模式,观察者,顾名思义,就是一个监 ...

  3. rdb map出错rbd sysfs write failed

    创建了一个rbd镜像 $ rbd create --size 4096 docker_test 然后,在Ceph client端将该rbd镜像映射为本地设备时出错. $ rbd map docker_ ...

  4. C#知识整理笔记

    这里简单介绍了一些常用的属性,以及一些术语的解释和举例说明,不太全面,希望读者多多补充. 1.重载:函数名相同,参数的个数或参数类型不同; public void MyDog(string s); p ...

  5. 配置apache,及mac下安装mysql

    先打开apache,在浏览器上输入  localhost     回车后会如果屏幕上显示:It works! 如下图: 这说明你的apache已开启 在window下配置apache: 1.找到apa ...

  6. wemall开源商城免费商城系统部分代码(内含代码地址)

    wemall开源商城免费商城系统部分代码,下面分享部分代码,供学习者学习: 开源版把install文件夹下的install.lock删除之后可进行自动安装 后台访问地址:http:// www.xxx ...

  7. 1687: [Usaco2005 Open]Navigating the City 城市交通

    1687: [Usaco2005 Open]Navigating the City 城市交通 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 94  Sol ...

  8. 1609: [Usaco2008 Feb]Eating Together麻烦的聚餐

    1609: [Usaco2008 Feb]Eating Together麻烦的聚餐 Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 1010  Solv ...

  9. Omi应用md2site-0.5.0发布-支持动态markdown拉取解析

    写在前面 Md2site是基于Omi的一款Markdown转网站工具,使用简单,生成的文件轻巧,功能强大. 官网:http://alloyteam.github.io/omi/md2site/ Git ...

  10. click和onclick本质的区别

    原生javascript的click在w3c里边的阐述是DOM button对象,也是html DOM click() 方法,可模拟在按钮上的一次鼠标单击. button 对象代表 HTML 文档中的 ...