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

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

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

    所以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来遍历(我相信来看的人都懂吧...),至于合并,最优化的方式就是利用并查集来合并两个节点。

    下面上伪代码:

Tarjan(u)//marge和find为并查集合并函数和查找函数
{
for each(u,v) //访问所有u子节点v
{
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算法有更深层次的理解了呢?

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

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

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

    所以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来遍历(我相信来看的人都懂吧...),至于合并,最优化的方式就是利用并查集来合并两个节点。

    下面上伪代码:

LCA-tarjan understand的更多相关文章

  1. HDU 2874 Connections between cities(LCA Tarjan)

    Connections between cities [题目链接]Connections between cities [题目类型]LCA Tarjan &题意: 输入一个森林,总节点不超过N ...

  2. POJ 1986 Distance Queries(LCA Tarjan法)

    Distance Queries [题目链接]Distance Queries [题目类型]LCA Tarjan法 &题意: 输入n和m,表示n个点m条边,下面m行是边的信息,两端点和权,后面 ...

  3. LCA Tarjan方法

    LCA Tarjan方法 不得不说,高中生好厉害,OI大佬,感觉上个大学好憋屈啊! 说多了都是眼泪 链接拿去:http://www.cnblogs.com/JVxie/p/4854719.html

  4. LCA tarjan+并查集POJ1470

    LCA tarjan+并查集POJ1470 https://www.cnblogs.com/JVxie/p/4854719.html 不错的一篇博客啊,让我觉得LCA这么高大上的算法不是很难啊,嘻嘻嘻 ...

  5. hihoCoder #1067 : 最近公共祖先·二 [ 离线LCA tarjan ]

    传送门: #1067 : 最近公共祖先·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站 ...

  6. LA 5061 LCA tarjan 算法

    题目大意: 给定所有点的权值都为0,给定一棵树以后,每次询问都要求给定两点 x , y 和一个权值w,要求x,y路径上所有点权值加上w,最后求出每一个节点的值 这里因为查询和点都特别多,所以希望能最后 ...

  7. [CF 191C]Fools and Roads[LCA Tarjan算法][LCA 与 RMQ问题的转化][LCA ST算法]

    参考: 1. 郭华阳 - 算法合集之<RMQ与LCA问题>. 讲得很清楚! 2. http://www.cnblogs.com/lazycal/archive/2012/08/11/263 ...

  8. 最近公共祖先LCA(Tarjan算法)的思考和算法实现

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

  9. POJ 1330 Nearest Common Ancestors(LCA Tarjan算法)

    题目链接:http://poj.org/problem?id=1330 题意:给定一个n个节点的有根树,以及树中的两个节点u,v,求u,v的最近公共祖先. 数据范围:n [2, 10000] 思路:从 ...

  10. Network POJ - 3694 (LCA+tarjan+桥)

    题目链接:https://vjudge.net/problem/POJ-3694 具体思路:首先可以通过缩点的方式将整个图变成一个树,并且树的每条边是桥,但是我们可以利用dfn数组将整个图变成树,这样 ...

随机推荐

  1. 深入理解python中函数传递参数是值传递还是引用传递

    深入理解python中函数传递参数是值传递还是引用传递 目前网络上大部分博客的结论都是这样的: Python不允许程序员选择采用传值还是传 引用.Python参数传递采用的肯定是"传对象引用 ...

  2. Git 常用命令大全-转载

    一. Git 常用命令速查 git branch 查看本地所有分支git status 查看当前状态 git commit 提交 git branch -a 查看所有的分支git branch -r ...

  3. Opencv中的WMesh

    费了半天劲,终于把这个WMesh类搞懂了,可惜效果不佳,比Matlab中的mesh差多了. 使用WMesh前,需要有一个Mesh对象,Mesh是三维数据点的基本几何信息.颜色信息.索引信息等集成的对象 ...

  4. jmeter设置中文语言

    1.在jmeter的bin目录下找到  jmeter.properties  文件并打开 2.搜索关键字 “language”,将37行(以搜索到的位置为准)改成下图所示:language=zh_CN ...

  5. WEB框架初识

    HTTP介绍 Hyper Text Transfer Protocol,超文本传输书协议,是万维网数据通信的基础,规定了请求和响应标准. HTTP工作原理 HTTP 请求以及响应的步骤 客户端连接到W ...

  6. 【机器学习】Learning to Rank 简介

    Learning to Rank 简介 去年实习时,因为项目需要,接触了一下Learning to Rank(以下简称L2R),感觉很有意思,也有很大的应用价值.L2R将机器学习的技术很好的应用到了排 ...

  7. 【DSP开发】C6678的中断控制器

    分两层,一层是每个core内部的中断控制器,这个叫interrupt controller,简写intc:一层是整个芯片的,属于芯片级的,在每个core的外面,这个叫chip-level interr ...

  8. python 求从1加到100的和,join的用法

    li=[] def func3(x): li.append(str(x)) if x==1: return 1 return x+func3(x-1) # print(func3(100)) re=f ...

  9. idea - maven子工程找不到父工程pom

    1.应该先构建父项目,再构建子项目.因为子项目依赖于父项目.即父项目先install到本地

  10. C++学习 之 指针及动态内存分配(笔记)

    1. 指针 1.1 指针的含义: 简单来说,指针是存储内存地址的变量.当我们声明了一个指针变量后,使用该指针时系统会根据指针内的地址索引内存块,读取内存内的值.指针因为是用来存地址的,所以一般固定长度 ...