原文地址:https://blog.csdn.net/qq_16234613/article/details/77431043

一、解释

在有向图G中,如果两个顶点间至少存在一条互相可达路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。 
求解有向图的强连通分量算法有很多,例如Kosaraju,Gabow和Tarjan算法,其中Gabow和Tarjan算法时间复杂度要优于Kosaraju。 
理解: 
如果单纯将其看出图的话有点难以理解,但是当我们将其看成树,就很容易了。 
 
如上图,如果两个点成强联通,那么显然在树中就会存在一个环,图中L-M-J-L和A-L-M-B-A成环所以组成的强联通分量。

二、Tarjan算法

Tarjan算法基于深度优先搜索树,其有两个重要变量DFN[u]:表示在深度搜索中遍历到该节点的次序。LOW(u)表示以u节点为树根,u及u以下树节点所能找到的最小次序号。注意Tarjan认为单个节点自身就是一个强联通分量,在处理数据时注意屏蔽。以上图为例,我们从A开始, 
A:DFN[1] = 1; LOW(1)=1 
L:DFN[2] = 2; LOW(2)=2 
M:DFN[3] = 3; LOW(3)=3 
J:DFN[4] = 4; LOW(4)=4 
这时我们在J节点继续往下搜索时,发现L节点我们已经搜索过了,且L:LOW(2)=2,我们发现J:LOW(4)=4>L:LOW(2)=2,因此我们将其赋值LOW(4)=2,这说明此时我们发现了一个环,代表一个强联通分量。 
下面继续: 
J:DFN[4] = 4; LOW(4)=2 
M:DFN[3] = 3; LOW(3)=2 
B:DFN[5] = 4; LOW(5)=5 
发现B到A: 
B:DFN[5] = 4; LOW(5)=1 
开始返回更新: 
M:DFN[3] = 3; LOW(3)=1 
L:DFN[2] = 2; LOW(2)=1 
A:DFN[1] = 1; LOW(1)=1 
发现DFN=LOW(1),弹出栈。

void tarjan(int u){

    DFN[u]=LOW[u]=++time; //次序从1开始,初始时由于默认将DFN[u]=LOW[u]都置为次序号
// 将当前节点压栈,置位在栈中,已访问。
visit[u]=;
s.push(u);
instack[u]=; //取u节点的下一路径节点v,当没有v可取时也说明深度搜索已经到达当前最底部,这是我们函数返回寻找另一条路径。
for(int j=;j<G[u].size();j++){
int v=G[u][j];
if(visit[v]==){
tarjan(v);
// 在深度搜索返回时,如果v节点下存在子树,要将u节点的LOW[u]更新。
LOW[u]=min(LOW[u],LOW[v]);
}
else if(instack[v]){
// v节点已经被访问,并且在栈中,说明在当前路径上存在环,此处只是赋值,但并不代表在u子树的底下的多个节点没有比当前环更大的环。无法作为深度终止条件。
LOW[u]=min(LOW[u],DFN[v]);
}
} int m;
int num=; //对一个环计数计数
// 在深度搜索完结后返回时,判断DFN[u]==LOW[u],相等说明找到了一个环,将栈中节点弹出。注意tarjan算法认为单个节点也为环。
if(DFN[u]==LOW[u]){
// 将栈中节点弹出,并计数
do{
m=s.top();
s.pop();
instack[m]=;
num++;
}while(m!=u); // 只有环内节点数大于两个才是真正环。
if(num>){
// n个点两两相交(互相到达),则有n*(n-1)/2条连接线
total+=num*(num-)/;
}
} }

关于为啥只用访问一次: 
开始疑惑,肯定会多条路径通过某一点,如果用visit记录访问记录的话,下一条路径不就会不能访问该点了吗?遂绘制丑图: 
 
如图当我们访问到6节点时发现有环,且到达底点,这时根据算法开始返回,同时将2-6-5这条环也遍历掉(此时5号已访问压栈且有LOW=1)。也就是说在返回到1号节点开始出栈时,我们已经把1号节点的子树全部访问了一遍,该成环的也做了标记。在1号节点下的子节点不会通向1号节点以上的节点,比如0号节点,不然1号只能算一个类似于2-6-5这条环。至于从0号到5号就不用再判断了。所以遍历一遍就行。我觉得巧妙之处在于在深度向前搜索过程并没有处理数据,而在深度返回过程中开始更新数据,记录找到的回路,并且到达子树根节点DFN[u]==LOW[u]才开始出栈。

图之强连通、强连通图、强连通分量 Tarjan算法的更多相关文章

  1. 有向图强连通分量Tarjan算法

    在https://www.byvoid.com/zhs/blog/scc-tarjan中关于Tarjan算法的描述非常好,转述如下: 首先解释几个概念: 有向图强连通分量:在有向图G中,如果两个顶点间 ...

  2. POJ1236_A - Network of Schools _强连通分量::Tarjan算法

    Time Limit: 1000MS   Memory Limit: 10000K Description A number of schools are connected to a compute ...

  3. 图的连通性:有向图强连通分量-Tarjan算法

    参考资料:http://blog.csdn.net/lezg_bkbj/article/details/11538359 上面的资料,把强连通讲的很好很清楚,值得学习. 在一个有向图G中,若两顶点间至 ...

  4. 求图的强连通分量--tarjan算法

    一:tarjan算法详解 ◦思想: ◦ ◦做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)用low[i]表示i节点DFS过程中i的下方节点所能到达的开始时间 ...

  5. 有向图强连通分量 Tarjan算法

    [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极 ...

  6. 图论-强连通分量-Tarjan算法

    有关概念: 如果图中两个结点可以相互通达,则称两个结点强连通. 如果有向图G的每两个结点都强连通,称G是一个强连通图. 有向图的极大强连通子图(没有被其他强连通子图包含),称为强连通分量.(这个定义在 ...

  7. 强连通分量——tarjan算法

    概念: 有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通.如果有向图G的每两个顶点都强连 ...

  8. 【有向图】强连通分量-Tarjan算法

    好久没写博客了(都怪作业太多,绝对不是我玩的太嗨了) 所以今天要写的是一个高大上的东西:强连通 首先,是一些强连通相关的定义 //来自度娘 1.强连通图(Strongly Connected Grap ...

  9. [有向图的强连通分量][Tarjan算法]

    https://www.byvoid.com/blog/scc-tarjan 主要思想 Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树.搜索时,把当前搜索树中未处理的 ...

随机推荐

  1. python inspect.stack() 的简单使用

    1. #python # -*- encoding: utf-8 -*- #获取函数的名字 import inspect def debug(): callnamer = inspect.stack( ...

  2. ingress-nginx 添加https证书

    1.配了一个证书,发现报错: kubectl logs  ingress-nginx-controller-96fnv   -n ingress-nginx unexpected error vali ...

  3. LOJ2538 PKUWC2018 Slay the Spire DP

    传送门 不想放题面了,咕咕咕咕咕 这个期望明明是用来吓人的,其实要算的就是所有方案的最多伤害的和. 首先可以知道的是,能出强化牌就出强化牌(当然最后要留一张攻击牌出出去),且数字尽量大 所以说在强化牌 ...

  4. Bootstrap Search Suggest 下拉框模糊查询

    源码地址:https://github.com/lzwme/bootstrap-suggest-plugin 有时间会完善!暂时有点忙!

  5. 【终结版】C#常用函数和方法集汇总

    C#里面的常用的函数和方法非常重要,然而做题的时候会经常忘记这些封装好的方法,所以我总结一下 C#常用函数和方法集. [1]C#操作字符串的常用使用方法 在 C# 中,您可以使用字符数组来表示字符串, ...

  6. Linux 磁盘与磁盘分区

    Linux 系统中所有的硬件设备都是通过文件的方式来表现和使用的,我们将这些文件称为设备文件,硬盘对应的设备文件一般被称为块设备文件.本文介绍磁盘设备在 Linux 系统中的表示方法以及如何创建磁盘分 ...

  7. Ansible之playbook的使用总结 - 运维笔记

    之前详细介绍了Ansible的安装, 配置, 以及Ansible常用模块的使用. 下面对Ansible的playbook用法做一小结. 为什么引入playbook?一般运维人员完成一个任务, 比如安装 ...

  8. ACM找bug方案

    测试数据和一些常见的数据都通过了然而还是wrong,可以试试下面的一些解决方案: 1.数据爆掉 ①  可以改变数据类型,以容纳 ②  修改当前算法,比如a*a/b可以改写成a/b*a 2 特殊情况,例 ...

  9. 《Linux内核分析》实践4

    <Linux内核分析> 实践四--ELF文件格式分析 20135211李行之 一.概述 1.ELF全称Executable and Linkable Format,可执行连接格式,ELF格 ...

  10. Software Engineering homework2

    现在市面上有诸多软件,选取一类软件,请分析: Q1:此类软件是什么时候出现的,这些软件是怎么说服你(陌生人)成为它们的用户的?他们的目标都是盈利的么?他们的目标都是赚取用户的现金的么?还是别的? A1 ...