Tarjan算法初步
一、前置知识:
强连通分量:有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大(看清是极大,不是最大)强连通子图,称为强连通分量(strongly connected components)。一个点x,若没有点与它强连通,则它自己也是一个强连通分量。
二、算法简述
Tarjan算法是一个主要用于求有向图的强连通分量或无向图的环的算法。(当然也有很多扩展,与其他一些神奇的算法搭配可能会碰撞出奇妙的火花)
(无向图的极大连通子图就不叫强连通分量了,叫连通分量。实际上求无向图的连通分量用bfs就行了(毕竟无向图没有方向,只要能到达,就是连通,也没有强连通这一说))
三、原理:
想象一个情景:从一个点u出发,一直向下遍历,然后忽得找到一个点,那个点x竟然有条指回点u的边!
那么想必这个点u能够从自身出发再回到自身
想必这个点u和其他向下遍历的该路径上的所有点构成了一个环,
想必这个环上的所有点都是强联通的。
但只是强联通啊,我们需要求的可是强连通分量啊......怎么在退回到这个点的时候,知道所有和这个点u构成强连通分量的点呢?
开个栈记录就行了。我们建一个栈,保证回溯到u时栈中u及u上面的点组成一个强连通分量,然后把它们弹出、记录就好了。
似乎做法已经明了了,用程序应该怎么实现呢?
四、程序实现:
首先需要介绍一些辅助数组
(1)、dfn[ ],表示这个点在dfs时是第几个被搜到的。
(2)、low[ ],表示这个点以及其子孙节点连的这个点及其祖先中dfn最小的值
(3)、stack[ ],表示当前所有可能能构成强连通分量的点。
(4)、vis[ ],表示一个点是否在stack[ ]数组中。
那么按照之上的思路,我们来考虑这几个数组的用处以及算法的具体过程。
假设现在开始遍历点u:
首先初始化dfn[u]=low[u]=第几个被dfs到
dfn可以理解,但为什么low也要这么做呢?
因为low的定义如上,也就是说如果没有子孙与u的祖先相连的话,dfn[u]一定是它和它的所有子孙中dfn最小的(因为它的所有子孙一定比他后搜到)。将u存入stack[ ]中,并将vis[u]设为true
stack[ ]有什么用?
如果u在stack中,u之后的所有点在u被回溯到时u和栈中所有在它之后的点都构成强连通分量。(也就是上文中所说的开个栈记录)遍历u的每一个能到的点,如果这个点dfn[ ]为0,即仍未访问过,那么就对点v进行dfs,然后low[u]=min{low[u],low[v]}
low[ ]有什么用? 应该能看出来吧,就是记录一个点它最大能连通到哪个祖先节点(当然包括自己)
如果从u遍历这个点之前这个点就被遍历到了,那么看它当前有没有在stack[ ]里,如果有(要么这个点是u的祖先,要么这个点与u的某个祖先强连通,反正这个点能到达u),说明这个点肯定能到达u,同样u能到达他,他俩强联通,那么low[u]=min{low[u],low[v]}
如果已经被弹掉了,说明无论如何这个点也不能与u构成强连通分量,因为它不能到达u(当处理强连通分量时才将元素弹出栈。处理包含这个点的强连通分量时没有处理掉u,就说明u不在它的强连通分量里)假设我们已经dfs完了u的所有的子树,那么之后无论我们再怎么dfs,u点的low值已经不会再变了。
那么如果dfn[u]=low[u]这说明了什么呢?
再结合一下dfn和low的定义来看看吧
dfn表示u点被dfs到的时间,low表示u和u所有的子树所能到达的u的祖先中dfn最小的。
这说明了u点及u点之下的所有子节点没有边是指向u的祖先的了,即我们之前说的u点与它的还在栈中的子孙节点构成了一个最大的强连通图即强连通分量
此时我们得到了一个强连通分量,把所有的u点以后压入栈中的点和u点一并弹出,将它们的vis[ ]置为false,如有需要也可以给它们染上相同颜色(后面会用到,用于缩点等等)
代码大概长成这样


对了,tarjan一遍不能搜完所有的点,因为存在孤立点或者其他
所以我们要对一趟跑下来还没有被访问到的点继续跑tarjan
怎么知道这个点有没有被访问呢?
看看它的dfn是否为0!

非常简短的tarjan复杂度证明:
思考每个点最多被dfs一次,所以均摊下来复杂度是O(n)的
证毕
五、扩展:
tarjan缩点:
1.什么时候要用缩点
众所周知,有向无环图总是有着一些蜜汁优越性,因为没有环,你可以放心的在上面跑dfs,搞DP,但如果是一张有向有环图,事情就会变得尴尬起来了
思考一下会发现如果不打vis标记就会t飞(一直在环里绕啊绕),但是如果打了,又不一定能保证最优解
而你一看题目却发现显然根据一些贪心的原则,这个环上每个点的最大贡献都是整个环的总贡献
这个时候缩点就显得很有必要了,因为单个点的贡献和整个环相同,为什么不去把整个环缩成一个超级点呢?
这个环只是为了好理解,事实上他应该是一个强连通分量,显然如果只缩掉一个强连通图,图中仍然有环存在
缩点的一个栗子
-----------> 
2.怎么缩点
还记得之前tarjan里的染色吗?
我们只需要把同一颜色的点权加到一块,然后把该颜色指向不同颜色的边建好就可以了
代码就不贴了,因为不同的题有不同的处理方法
无向图tarjan求环:
每次tarjan递归时记录父亲节点到儿子节点走的边的对应相反边(因为无向图对于一条边用前向星存的话要插入2次),若儿子在不走这条边的情况下仍能得到小于dfn的low,即可走另一条路径到达父亲节点的祖先,说明有一个环。并且若干个相交的环会以一个强连通分量的形式呈现出来。
无向图tarjan求割点:www.cnblogs.com/collectionne/p/6847240.html
该博客有一处low的维护操作与普通tarjan不同:low[u] = min(low[u], dfn[v]);
这里解释一下:若v已经被遍历过了,这时遍历到u发现u与v有连边。这说明什么?v还没有被回溯。因为这时无向图,既然u能到v,那v也能到u,当回溯到v时,v所能到的点必然都已经被遍历完了。这里刚遍历到u,不就说明v还没有被回溯到嘛。这样,v不就是u的祖先了嘛。
大量摘自洛谷博客:初探tarjan算法(求强连通分量),略有修改。在此对作者:Styx 表示真挚的感谢。
Tarjan算法初步的更多相关文章
- 【原创】tarjan算法初步(强连通子图缩点)
[原创]tarjan算法初步(强连通子图缩点) tarjan算法的思路不是一般的绕!!(不过既然是求强连通子图这样的回路也就可以稍微原谅了..) 但是研究tarjan之前总得知道强连通分量是什么吧.. ...
- 强连通分量与tarjan算法初步运用
模板题:B3609 [图论与代数结构 701] 强连通分量 题目描述 给定一张 n 个点 m 条边的有向图,求出其所有的强连通分量. 注意,本题可能存在重边和自环. 输入格式 第一行两个正整数 n , ...
- 图论初步-Tarjan算法及其应用
暑假刷了一堆Tarjan题到头来还是忘得差不多. 这篇博客权当复习吧. 一些定义 无向图 割顶与桥 (划重点) 图G是连通图,删除一个点表示删除此点以及所有与其相连的边. 若删除某点u后G不再连通,那 ...
- 有向图强连通分量的Tarjan算法
有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G ...
- 点/边 双连通分量---Tarjan算法
运用Tarjan算法,求解图的点/边双连通分量. 1.点双连通分量[块] 割点可以存在多个块中,每个块包含当前节点u,分量以边的形式输出比较有意义. typedef struct{ //栈结点结构 保 ...
- 割点和桥---Tarjan算法
使用Tarjan算法求解图的割点和桥. 1.割点 主要的算法结构就是DFS,一个点是割点,当且仅当以下两种情况: (1)该节点是根节点,且有两棵以上的子树; (2)该节 ...
- Tarjan算法---强联通分量
1.基础知识 在有向图G,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子 ...
- (转载)LCA问题的Tarjan算法
转载自:Click Here LCA问题(Lowest Common Ancestors,最近公共祖先问题),是指给定一棵有根树T,给出若干个查询LCA(u, v)(通常查询数量较大),每次求树T中两 ...
- 强连通分量的Tarjan算法
资料参考 Tarjan算法寻找有向图的强连通分量 基于强联通的tarjan算法详解 有向图强连通分量的Tarjan算法 处理SCC(强连通分量问题)的Tarjan算法 强连通分量的三种算法分析 Tar ...
随机推荐
- TCP listener
调试时未关闭之前开启的TCP SERVER: 应该关闭: TcpListener TcpClient 1.开启TCP listener (1)Start()方法失败 异常位置:该异常的产生位置为 tc ...
- Laravel5.8自定义函数存放位置
1. 创建文件 app/helpers.php <?php // 示例函数 function foo() { return "foo"; } 2. 修改项目 composer ...
- 邀请好友注册页面光标点到输入框后,输入框会先灰一下。只有ios存在
输入框会先灰一下.只有ios存在 用这一行代码可以解决问题: -webkit-tap-highlight-color:transparent;
- 两张超级大表join优化
一个简单的两表关联,SQL跑了差不多一天一夜,这两个表都非常巨大,每个表都有几十个G,数据量每个表有20多亿,表的字段也特别多. 相信大家也知道SQL慢在哪里了,单个进程的PGA 是绝对放不下几十个G ...
- oracle函数nvl,nvl2的区别,nullif函数,coalesce函数
在oracle中用nvl和nvl2函数来解决为空的情况,例如,如果奖金为空,则为它指定一个数.也就是nvl(奖金字段,指定的奖金),但是两个的类型要一致. 1)nvl()函数 SQL> sele ...
- Vue2 & ElementUI实现管理后台之input获得焦点
Vue.directive('focus', function (el, option) { var defClass = 'el-input', defTag = 'input'; var valu ...
- 企业级开发账号In House ipa发布流程
这两天需要发布一个ipa放到网上供其他人安装,需要用到企业级开发者账号.在网上查了一下资料,感觉没有一个比较完善的流程,于是决定把整个流程写下来,供大家参考. 首先详细说明一下我们的目标,我们需要发布 ...
- python 单引号、双引号和三引号混用
单引号: 当单引号中存在单引号时,内部的单引号需要使用转义字符,要不然就会报错: 当单引号中存在双引号时,双引号可以不用加转义字符,默认双引号为普通的字符,反之亦然. 双引号: 当双引号中存在双引号时 ...
- adb shell命令模拟按键/输入input使用keycode 列表详解
在adb shell里有一个非常使用的命令,模拟按键输入,这里首先不要理解为是键盘的模拟按键,下面命令的使用和键值做一个详解. input命令格式 adb shell input keyevent & ...
- 023-zabbix性能优化中的几个中肯建议
随着zabbix的广泛应用,少数人的zabbix服务器在性能上出现瓶颈,或者在未来会出现性能方面的瓶颈,接下来讨论几个有效并且简单的优化方案. 服务器硬件 想通过几个简单的配置让服务器提高成倍的性能, ...