Tarjan详解

“tarjan陪伴强联通分量
生成树完成后思路才闪光
欧拉跑过的七桥古塘
让你 心驰神往”----《膜你抄》

一、tarjan求强连通分量

  1. 什么是强连通分量?

引用来自度娘的一句话:

“有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。
如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。”

一脸懵逼......不过倒也不难理解。

反正就是在图中找到一个最大的图,使这个图中每个两点都能够互相到达。这个最大的图称为强连通分量,同时一个点也属于强连通分量。

如图中强连通分量有三个:1-2-3,4,5

  1. 强连通分量怎么找?

噫......当然,通过肉眼可以很直观地看出1-2-3是一组强连通分量,但很遗憾,机器并没有眼睛,所以该怎么判断强连通分量呢?

如果仍是上面那张图,我们对它进行dfs遍历。

可以注意到红边非常特别,因为如果按照遍历时间来分类的话,其他边都指向在自己之后被遍历到的点,而红边指向的则是比自己先被遍历到的点。

如果存在这么一条边,那么我们可以yy一下,emmmm.......

从一个点出发,一直向下遍历,然后忽得找到一个点,那个点竟然有条指回这一个点的边!

那么想必这个点能够从自身出发再回到自身

想必这个点和其他向下遍历的该路径上的所有点构成了一个环,

想必这个环上的所有点都是强联通的。

但只是强联通啊,我们需要求的可是强连通分量啊......

那怎么办呢?

我们还是yy出那棵dfs树

不妨想一下,什么时候一个点和他的所有子孙节点中的一部分构成强连通分量?

他的子孙再也没有指向他的祖先的边,却有指向他自己的边

因为只要他的子孙节点有指向祖先的边,显然可以构成一个更大的强联通图。

比如说图中红色为强连通分量,而蓝色只是强联通图

那么我们只需要知道这个点u下面的所有子节点有没有连着这个点的祖先就行了。

但似乎还有一个问题啊......

我们怎么知道这个点u它下面的所有子节点一定是都与他强联通的呢?

这似乎是不对的,这个点u之下的所有点不一定都强联通

那么怎么在退回到这个点的时候,知道所有和这个点u构成强连通分量的点呢?

开个记录就行了

什么?!这么简单?

没错~就是这么简单~

如果在这个点之后被遍历到的点已经能与其下面的一部分点(也可能就只有他一个点)已经构成强连通分量,即它已经是最大的。

那么把它们一起从栈里弹出来就行了。

所以最后处理到点u时如果u的子孙没有指向其祖先的边,那么它之后的点肯定都已经处理好了,一个常见的思想,可以理解一下。

所以就可以保证栈里留下来u后的点都是能与它构成强连通分量的。

似乎做法已经明了了,用程序应该怎么实现呢?

所以为了实现上面的操作,我们需要一些辅助数组

  • (1)、dfn[ ],表示这个点在dfs时是第几个被搜到的。
  • (2)、low[ ],表示这个点以及其子孙节点连的所有点中dfn最小的值
  • (3)、stack[ ],表示当前所有可能能构成是强连通分量的点。
  • (4)、vis[ ],表示一个点是否在stack[ ]数组中。

那么按照之上的思路,我们来考虑这几个数组的用处以及tarjan的过程。

假设现在开始遍历点u:

(1)、首先初始化dfn[u]=low[u]=第几个被dfs到

dfn可以理解,但为什么low也要这么做呢?

因为low的定义如上,也就是说如果没有子孙与u的祖先相连的话,dfn[u]一定是它和它的所有子孙中dfn最小的(因为它的所有子孙一定比他后搜到)。

(2)、将u存入stack[ ]中,并将vis[u]设为true

stack[ ]有什么用?

如果u在stack中,u之后的所有点在u被回溯到时u和栈中所有在它之后的点都构成强连通分量。

(3)、遍历u的每一个能到的点,如果这个点dfn[ ]为0,即仍未访问过,那么就对点v进行dfs,然后low[u]=min{low[u],low[v]}

low[ ]有什么用?

应该能看出来吧,就是记录一个点它最大能连通到哪个祖先节点(当然包括自己)

如果遍历到的这个点已经被遍历到了,那么看它当前有没有在stack[ ]里,如果有那么low[u]=min{low[u],low[v]}

如果已经被弹掉了,说明无论如何这个点也不能与u构成强连通分量,因为它不能到达u

如果还在栈里,说明这个点肯定能到达u,同样u能到达他,他俩强联通。

(4)、假设我们已经dfs完了u的所有的子树那么之后无论我们再怎么dfs,u点的low值已经不会再变了。

那么如果dfn[u]=low[u]这说明了什么呢?

再结合一下dfn和low的定义来看看吧

dfn表示u点被dfs到的时间,low表示u和u所有的子树所能到达的点中dfn最小的。

这说明了u点及u点之下的所有子节点没有边是指向u的祖先的了,即我们之前说的u点与它的子孙节点构成了一个最大的强连通图即强连通分量

此时我们得到了一个强连通分量,把所有的u点以后压入栈中的点和u点一并弹出,将它们的vis置为false,如有需要也可以给它们打上相同标记(同一个数字)

tarjan到此结束

参考博文:https://www.cnblogs.com/stxy-ferryman/p/7779347.html

原文地址:https://www.cnblogs.com/hulean/p/10856030.html

时间: 2024-10-16 00:34:32

Tarjan详解的相关文章

LCA离线算法Tarjan详解

离线算法也就是需要先把所有查询给保存下来,最后一次输出结果. 离线算法是基于并查集实现的,首先就是初始化P[i] = i. 接下来对于每个点进行dfs: ①首先判断是否有与该点有关的查询,如果当前该点为u,与它有关的点为v,如果v已经访问过了,那么它们的LCA就是find(v).如果v还没有访问,那就不用管它. ②对该点的子节点继续dfs,需要注意的是,dfs完之后需要需要p[v]=u,将v点并到其父亲节点上. 1 void LCA(int u) 2 { 3 vis[u]=1; 4 for(in

Tarjan算法详解

Tarjan算法详解 [概念] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. [功能] Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连

LR杂记 - Linux系统监控工具之vmstat详解

题意:求一个无向图的,去掉两个不同的点后最多有几个连通分量. 思路:枚举每个点,假设去掉该点,然后对图求割点后连通分量数,更新最大的即可.算法相对简单,但是注意几个细节: 1:原图可能不连通. 2:有的连通分量只有一个点,当舍去该点时候,连通分量-1: 复习求割点的好题! #include<iostream> #include<cstdio> #include<vector> using namespace std; int n,m; vector<vector&

并查集算法详解

更好的阅读体验 并查集算法详解 算法详解 维护类型 身为一个数据结构,我们的并查集,它的维护对象是我们的关注点. 并查集适合维护具有非常强烈的传递性质,或者是连通集合性质. 性质详解 传递性质 传递性,也就是具有传递效应的性质,比如说A传递给B一个性质或者条件,让B同样拥有了这个性质或者条件,那么这就是我们所说的传递性. 连通集合性质 连通集合性,和数学概念上的集合定义是差不多的, 比如说A和B同属一个集合,B和C同属一个集合,那么A,B,C都属于同一个集合.这就是我们所谓的连通集合性质. 算法

ST表超级详解

ST表超级详解 关于ST表,有很多文章,这里本蒟蒻也来发一波~~ 希望能为您提供帮助~~ 1.ST表的介绍 ST表算法全称Sparse-Table算法,是由Tarjan提出的一种解决RMQ问题(区间最值)的强力算法.离线预处理时间复杂度 θ(nlogn),在线查询时间 θ(1),可以说是一种非常高效的算法.不过ST表的应用场合也是有限的,它只能处理静态区间最值,不能维护动态的,也就是说不支持在预处理后对值进行修改.一旦修改,整张表便要重新计算,时间复杂度极高.动态最值可以用线段树.树状数组等来维

Spring事务管理(详解+实例)

写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

转载:DenseNet算法详解

原文连接:http://blog.csdn.net/u014380165/article/details/75142664 参考连接:http://blog.csdn.net/u012938704/article/details/53468483 本文这里仅当学习笔记使用,具体细节建议前往原文细度. 论文:Densely Connected Convolutional Networks 论文链接:https://arxiv.org/pdf/1608.06993.pdf 代码的github链接:h

MariaDB(MySQL)创建、删除、选择及数据类型使用详解

一.MariaDB简介(MySQL简介略过) MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品.在存储引擎方面,使用XtraDB(英语:XtraDB)来代替MySQL的InnoDB. MariaDB由MySQL的创始人Michael Widenius(英语:Michael Widenius)主导开发,他早前曾以10亿美元的价格,将自己创建的公司MySQL A

HttpServletResponse和HttpServletRequest详解

HttpServletResponse,HttpServletRequest详解 1.相关的接口 HttpServletRequest HttpServletRequest接口最常用的方法就是获得请求中的参数,这些参数一般是客户端表单中的数据.同时,HttpServletRequest接口可以获取由客户端传送的名称,也可以获取产生请求并且接收请求的服务器端主机名及IP地址,还可以获取客户端正在使用的通信协议等信息.下表是接口HttpServletRequest的常用方法. 说明:HttpServ