理论: 图论(14):最大强连通图算法 tarjan
来源:互联网 发布:依爱消防主机设备编程 编辑:程序博客网 时间:2024/06/05 06:54
最大强连通图定义
在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。
朴素算法
根据定义我们不难想到, 对同一张图同时进行正反两次遍历, 对两次的遍历结果取交集, 这里得到的便是强连通图。 在强连通图中寻找到度数最大的图即时最大强连通图。 同时的正反两次遍历我们不难发现他的时间复杂度达到了O(N ^ 2 + M)
tarjan算法
算法思想:
用dfs遍历G中的每个顶点,通dfn[i]表示dfs时达到顶点i的时间,low[i]表示i所能直接或间接达到时间最小的顶点。(实际操作中low[i]不一定最小,但不会影响程序的最终结果)
程序开始时,time初始化为0,在dfs遍历到v时,low[v]=dfn[v]=time++,
v入栈(这里的栈不是dfs的递归时系统弄出来的栈)扫描一遍v所能直接达到的顶点k,如果 k没有被访问过那么先dfs遍历k,low[v]=min(low[v],low[k]);如果k在栈里,那么low[v]=min(low[v],dfn[k])(就是这里使得low[v]不一定最小,但不会影响到这里的low[v]会小于dfn[v])。扫描完所有的k以后,如果low[v]=dfn[v]时,栈里v以及v以上的顶点全部出栈,且刚刚出栈的就是一个极大强连通分量。
大致证明过程
1.tarjan算法的基于定理:在任何深度优先搜索中,同一强连通分量内的所有顶点均在同一棵深度优先搜索树中。也就是说,强连通分量一定是有向图的某个深搜树子树。
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以上的顶点组成的子图是一个极大强连通分量。
3.因为dfn保存的是深搜树的节点编号, 所以栈中下方的点一定可以到达上方, 而low保存的是这个节点向后(栈的下方)指的最大距离, 也就是向后反向边的最大距离。 我们说到栈中的节点正向可达, 拥有着一条反向边之后, 这个栈中的这个区间的元素就成了一个环, 变成了任意两点可达。 也就是我们所说的强连通, 又因为tarjan算法会遍历所有的点, 所以这里的强连通经过不断取最大之后, 得到的就是最大强连通分量。
时间复杂度
在这里每个点进一次栈, 每条边被遍历一次。 所以总的确定执行次数时n + m。 即O(N + M).
原始模板
void tarjan(int i){ int j; DFN[i]=LOW[i]=++Dindex; instack[i]=true; Stap[++Stop]=i; for (edge *e=V[i];e;e=e->next) { j=e->t; if (!DFN[j]) { tarjan(j); if (LOW[j]<LOW[i]) LOW[i]=LOW[j]; } else if (instack[j] && DFN[j]<LOW[i]) LOW[i]=DFN[j]; } if (DFN[i]==LOW[i]) { Bcnt++; do { j=Stap[Stop--]; instack[j]=false; Belong[j]=Bcnt; } while (j!=i); }}void solve(){ int i; Stop=Bcnt=Dindex=0; memset(DFN,0,sizeof(DFN)); for (i=1;i<=N;i++) if (!DFN[i]) tarjan(i);}
动画演示
一下内容转自:https://www.byvoid.com/blog/scc-tarjan/
原始有向连通图:
从节点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}。
- 理论: 图论(14):最大强连通图算法 tarjan
- 强连通算法--Tarjan
- 有向图的强连通分量(tarjan算法)
- 强连通图tarjan算法模板题(HDU1269)
- poj 1236 强连通图 tarjan算法
- 强连通分量(tarjan算法)
- tarjan算法求图中环(强连通分量)
- Tarjan算法(求强连通分量)
- tarjan算法模板(强连通分量)
- tarjan 算法(求强连通分量)
- 图之强连通、强连通图、强连通分量 Tarjan算法
- 图论 tarjan 有向图 的 强连通分量(noip算法每周过)
- 强连通分量 Tarjan算法
- 强连通分量 tarjan算法
- 强连通分量Tarjan算法
- Tarjan强连通分量算法
- 强连通Tarjan算法入门
- 强连通分量Tarjan算法
- 安装DirectX SDK时出现Error Code:s1023 的解决方案
- android 代码判断设备是平板or手机
- (OK) 编译libiconv-1.14(静态库)—CentOS 7— android-ndk
- Android 学习笔记(10)—— Intent 基本运用
- 2款XSHELL配色方案及导入配色方案的方法
- 理论: 图论(14):最大强连通图算法 tarjan
- Android 界面方向设置
- 查询数据库sql执行的频率,以便设计出更好的数据库
- HDOJ 1269 迷宫城堡
- (OK) 编译xerces-c-3.1.2(动态库)—CentOS 7— android-ndk
- 合同的重要性
- C++ assert()的使用方法
- Android Studio NDK基础使用
- Android 不显示标题栏和全屏的设置方法(转载)