有向图计算的一些概念和方法

在有向图里求回路,利用深搜方法时候经常会碰到边的判断

一次深度搜索的时间复杂度为O(E),其中E为边的数目。

深搜过程中对有向图遍历得到的一棵搜索树,里面的树的边分为4种:

1、树边 Tree edge

2、向前边 Forward edge

3、向后边 Back edge

4、横叉边 Cross edge

在求强连通子集时候,也就是求向后边中包含的回路中所有的节点。这些节点可以用一个点来表示,构成新的有向无环图。

在求骨架矩阵的时候,就是把所有的向前边删除,删除向前边不影响整个系统中任何一个节点的可达性。

示意图http://www.93337.com/ism/edge.html

kosaraju算法

时间复杂度:O(M+N) 注:M代表边数,N代表顶点数。

这里没有把反图的时间算到里面。

步骤概要:

1. DFS有向图G,并以后根序记录节点

2. 把存在于记录集中且最后访问节点作为起点,DFS反图GT,并以先根序把节点从记录中剔除;

3. 若此次不能DFS反图GT所有节点,则重复步骤2,直到所有节点都被剔除出记录;每次剔除掉的节点集即为原有向图G的一个强连通分量

简要证明:

1. 第一次DFS有向图G时,最后记录下的节点必为最后一棵生成树的根节点。

证明:假设最后记录下节点不是树根,则必存在一节点为树根,且树根节点必为此节点祖先;而由后根序访问可知祖先节点比此节点更晚访问,矛盾;原命题成立

2. 第一次DFS的生成森林中,取两节点A、B,满足:B比A更晚记录下,且B不是A的祖先(即在第一次DFS中,A、B处于不同的生成树中);则在第二次DFS的生成森林中,B不是A的祖先,且A也不是B的祖先(即在第二次DFS中,A、B处于不同的生成树中)。

证明:假设在第二次DFS的生成森林中,B是A的祖先,则反图GT中存在B到A路径,即第一次DFS生成森林中,A是B的祖先,则A必比B更晚记录下,矛盾;假设在第二次DFS的生成森林中,A是B的祖先,则反图GT中存在A到B路径,即第一次DFS生成森林中,B是A的祖先,矛盾;原命题成立

3. 按上述步骤求出的必为强连通分量

证明:首先,证明2保证了第二次DFS中的每一棵树都是第一次DFS中的某棵树或某棵树的子树。其次,对于第二次DFS中的每棵树,第一次DFS保证了从根到其子孙的连通性,第二次DFS保证了根到子孙的反向连通性(即子孙到根的连通性);由此,此树中的每个节点都通过其根相互连通。

Tarjan算法

Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连通分量。强连通分量是指有向图G里顶点间能互相到达的子图。而如果一个强连通分量已经没有被其它强通分量完全包含的话,那么这个强连通分量就是极大强连通分量。

时间复杂度:O(M+N) 注:M代表边数,N代表顶点数。

步骤概要:

步骤1: 找一个没有被遍历过的顶点v,进行步骤2(v)(遍历时间由1开始累加,若是非连通图,则须重复进行步骤1)。否则,算法结束。

步骤2(v): 初始化dfn[v]和low[v]的值为当前的遍历时间,并且v进栈;

对于v所有的邻接顶点u:
(1) 如果u没有被遍历过,则进行步骤2(u),同时维护low[v]。
(2) 如果u已经被遍历过,但还在栈中(即还不属于任一强连通分量),则维护low[v],否则不做任何操作。
   如果有dfn[v]==low[v],则把栈中的顶点弹出(直到把v都弹出为止),这些顶点组成一个强连通分量。

简要证明:

1. 在栈里,当dfs遍历到v,而且已经遍历完v所能直接到达的顶点时,low[v]=dfn[v]时,v一定能到达栈里v上面的顶点:
因为当dfs遍历到v,而且已经dfs递归调用完v所能直接到达的顶点时(假设上面没有low=dfn),这时如果发现low[v]=dfn[v],栈上面的顶点一定是刚才从顶点v递归调用时进栈的,所以v一定能够到达那些顶点。

2 .dfs遍历时,如果已经遍历完v所能直接到达的顶点而low[v]=dfn[v],我们知道v一定能到达栈里v上面的顶点,这些顶点的low一定小于 自己的dfn,不然就会出栈了,也不会小于dfn[v],不然low [v]一定小于dfn[v],所以栈里v以其v以上的顶点组成的子图是一个强连通分量,如果它不是极大强连通分量的话low[v]也一定小于dfn[v](这里不再详细说),所以栈里v以其v以上的顶点组成的子图是一个极大强连通分量。

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

下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。

直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为O(N^2+M)。更好的方法是Kosaraju算法或Tarjan算法,两者的时间复杂度都是O(N+M)。本文介绍的是Tarjan算法。

[Tarjan算法]

Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。

定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。由定义可以得出,

Low(u)=Min
{
DFN(u),
Low(v),(u,v)为树枝边,u为v的父节点
DFN(v),(u,v)为指向栈中节点的后向边(非横叉边)
}

当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。

算法伪代码如下

tarjan(u)
{
DFN[u]=Low[u]=++Index                      // 为节点u设定次序编号和Low初值
Stack.push(u)                              // 将节点u压入栈中
for each (u, v) in E                       // 枚举每一条边
if (v is not visted)               // 如果节点v未被访问过
tarjan(v)                  // 继续向下找
Low[u] = min(Low[u], Low[v])
else if (v in S)                   // 如果节点v还在栈内
Low[u] = min(Low[u], DFN[v])
if (DFN[u] == Low[u])                      // 如果节点u是强连通分量的根
repeat
v = S.pop                  // 将v退栈,为该强连通分量中一个顶点
print v
until (u== v)
}

接下来是对算法流程的演示。

从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时,DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连通分量。

返回节点5,发现DFN[5]=LOW[5],退栈后{5}为一个强连通分量。

返回节点3,继续搜索到节点4,把4加入堆栈。发现节点4向节点1有后向边,节点1还在栈中,所以LOW[4]=1。节点6已经出栈,(4,6)是横叉边,返回3,(3,4)为树枝边,所以LOW[3]=LOW[4]=1。

继续回到节点1,最后访问节点2。访问边(2,4),4还在栈中,所以LOW[2]=DFN[4]=5。返回1后,发现DFN[1]=LOW[1],把栈中节点全部取出,组成一个连通分量{1,3,4,2}。

至此,算法结束。经过该算法,求出了图中全部的三个强连通分量{1,3,4,2},{5},{6}。

可以发现,运行Tarjan算法的过程中,每个顶点都被访问了一次,且只进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度为O(N+M)。

求有向图的强连通分量还有一个强有力的算法,为Kosaraju算法。Kosaraju是基于对有向图及其逆图两次DFS的方法,其时间复杂度也是O(N+M)。与Trajan算法相比,Kosaraju算法可能会稍微更直观一些。但是Tarjan只用对原图进行一次DFS,不用建立逆图,更简洁。在实际的测试中,Tarjan算法的运行效率也比Kosaraju算法高30%左右。此外,该Tarjan算法与求无向图的双连通分量(割点、桥)的Tarjan算法也有着很深的联系。学习该Tarjan算法,也有助于深入理解求双连通分量的Tarjan算法,两者可以类比、组合理解。

求有向图的强连通分量的Tarjan算法是以其发明者Robert Tarjan命名的。Robert Tarjan还发明了求双连通分量的Tarjan算法,以及求最近公共祖先的离线Tarjan算法,在此对Tarjan表示崇高的敬意。

Gabow算法

Gabow算法的用途之一是,求一个有向图G=(V,E)里极大强连通分量。强连通分量是指有向图G里顶点间能互相到达的子图。而如果一个强连通分量已经没有被其它强通分量完全包含的话,那么这个强连通分量就是极大强连通分量。

时间复杂度:O(M+N) 注:M代表边数,N代表顶点数。

步骤概要:

步骤1: 找一个没有被遍历过的顶点v,进行步骤2(v)(遍历时间由1开始累加,若是非连通图,则须重复进行步骤1)。否则,算法结束。

步骤2 将v压入堆栈stk1[]和stk2[];

对于v所有的邻接顶点u:
(1) 如果u没有被遍历过,则进行步骤2(u),同时维护stk2[]
(2) 如果u已经被遍历过,如果访问过,但没有删除,维护stk2[](处理环的过程,在stk2 中删除构成环的节点)
如果stk2[]的顶元素==v,那么输出相应的强连通分量

简要说明:

  这个算法其实就是Tarjan算法的变异体,我们观察一下,只是它用第二个堆栈来辅助求出强连通分量的根,而不是Tarjan算法里面的DFN[]和Low[]数组。那么,我们说一下如何使用第二个堆栈来辅助求出强连通分量的根。
  我们使用类比方法,在Tarjan算法中,每次Low[i]的修改都是由于环的出现(不然,Low[i]的值不可能变小),每次出现环,在这个环里面只剩下一个Low[i]没有被改变(深度最低的那个),或者全部被改变,因为那个深度最低的节点在另一个环内。那么Gabow算 法中的第二堆栈变化就是删除构成环的节点,只剩深度最低的节点,或者全部删除,这个过程是通过出栈来实现,因为深度最低的那个顶点一定比前面的先访问,那 么只要出栈一直到栈顶那个顶点的访问时间不大于深度最低的那个顶点。其中每个被弹出的节点属于同一个强连通分量。那有人会问:为什么弹出的都是同一个强连 通分量?因为在这个节点访问之前,能够构成强连通分量的那些节点已经被弹出了,这个对Tarjan算法有了解的都应该清楚,那么Tarjan算法中的判断根我们用什么来代替呢?想想,其实就是看看第二个堆栈的顶元素是不是当前顶点就可以了。
  现在,你应该明白其实Tarjan算法和Gabow算法其实是同一个思想的不同实现,但是,Gabow算法更精妙,时间更少(不用频繁更新Low[])。

解释结构模型ISM-2-5相关推荐

  1. ISM解释结构模型——研究系统结构关系情况

    一.解释结构模型ISM介绍 ISM(解释结构模型,Interpretative Structural Modeling Method,简称ISM方法)是一种系统工程研究方法,其作用在于研究系统结构关系 ...

  2. ISM解释结构模型法

    ISM解释结构模型法,并不知道是个啥,是风险分析方向的同学毕业要用到的,感觉不是很难,但是有着自己对应的原理,对于没有编程基础的同学来说可能有点难度. 我也忘了数据从哪来的了,不过计算的过程是没错的 ...

  3. 解释结构模型ISM-2-4

    可达矩阵判定强连通分量的方法   a b c d e f g h i j k l m n o a   1 1 1                     1 b                     ...

  4. 解释结构模型ISM-2-1

    系统的区域划分,矩阵连通性判定   子 丑 寅 卯 辰 巳 午 未 申 酉 戌 亥 子   1               1     丑           1   1     1   寅     ...

  5. 解释结构模型ISM-1-1

    利用Warshall转移闭包思想,快速迭代获得可达矩阵.步骤 传递闭包Warshall方法简要介绍 ① 在集合X上的二元关系R的传递闭包是包含R的X上的最小的传递关系.R的传递闭包在数字图像处理的图像 ...

  6. 解释结构模型ISM-1

    可达矩阵的求解 一个17 * 17 的方阵   a b c d e f g h i j k l m n o p q a   1                               b     ...

  7. 解释结构模型ISM-2-3

    环路缩减,及其表示   a b c d e f g h i j k l m n o a               1             1 b 1                       ...

  8. 基于ISM的大数据在建筑领域中的应用障碍分析

    点击上方蓝字关注我们 基于ISM的大数据在建筑领域中的应用障碍分析 纪颖波1, 赵子豪1, 姚福义2 1 北方工业大学土木工程学院,北京 100144 2 重庆大学管理科学与房地产学院,重庆 4000 ...

  9. 《大数据》2021年第6期目次摘要

    点击上方蓝字关注我们 <大数据> 第7卷第6期 2021年11月 大数据2021年第6期 (点击原文链接在官网阅读完整文章) 目次 01 专题导读:大数据支撑的智能应用 周斌, 秦永彬 0 ...

最新文章

  1. 深入挖掘Hyperledger Fabric中的私有数据
  2. 三张图读懂机器学习 :基本概念、五大流派与九种常见算法
  3. 【C++ 语言】引用 ( 引用简介 | 指针常量 | 常量指针 | 常引用 | 引用参数 | 引用 指针 对比 )
  4. 验证码的编写——本质:图片目的:防止恶意表单注册
  5. 【软考-软件设计师】程序设计的基本成分
  6. jquery插件之jquery-ui
  7. Logistic Regression:最基础的神经网络
  8. Jetty9 源码初解(2)——IO之Connection
  9. wifi卡慢延迟高_120平套三没网线,吃鸡延迟只有20ms,网件Orbi RBK50路由真香
  10. Linux系统启动需要多长时间,Linux系统启动时间的极限优化是怎样进行的?
  11. mysql 查询一年中每个月份的数据量
  12. spine骨骼动画基础一文通
  13. java 压缩js css_Java使用YUI Compressor压缩JS/CSS文件
  14. linux dma大块内存,宋宝华:Linux内核的连续内存分配器(CMA)——避免预留大块内存...
  15. 线性代数学习心得(二)矩阵的逆和矩阵变换
  16. js 如何实现点击一键复制文本
  17. VueJs笔记01-视频小码哥
  18. 伸缩的菜单html,javascript伸缩菜单栏实现代码分享
  19. #51CTO学院四周年#而立之年的不二之选
  20. 万能数据库查询分析器使用技巧之(十一)

热门文章

  1. 服务器远程端口是什么?远程端口怎么设置?
  2. 内网服务器外网连接SSH远程端口转发实战详解
  3. 自然语言处理笔记9-哈工大 关毅
  4. 【饭谈】面试官让你来个“自我介绍”,你准备怎么说?
  5. (王道计算机组成原理)第四章指令系统-第一节2:扩展操作码
  6. vba mysql 自动化错误_在Excel中运行VBA脚本时出现自动化错误
  7. 计算机技术271,色准到令人发指,对标UP2716D的HKC B7000专业级显示器评测
  8. 看图学中文版VS2010 Workflow 4.0 (五)
  9. 东数西算加快云网与数据融合 天翼云架起云间高速
  10. 本周上班6天,令人兴奋的五一小长假即将到来