有向图的强连通分量
有向图的强连通分量
『StronglyConnectedComponent』(SCC)
一、 Kosaraju算法
1、伪代码
Kosaraju_Algorithm:
step1:对原图G进行深度优先遍历,记录每个节点的离开时间。
step2:选择具有最晚离开时间的顶点,对反图GT进行遍历,删除能够遍历到的顶点,这些顶点构成一个强连通分量。
step3:如果还有顶点没有删除,继续step2,否则算法结束。
2、原理:
说明:设F(u)为第一次调用DFS时计算出的完成时间。F(U)=max{F(u)},U为顶点集。
定理:设C和C’为有向图G的两个不同的强联通分支。假设有一条边(u,v)属于E,其中u在C中,V在C’中,则F(C)>F(C’)。
由上述定理得出
推论:设C和C’为有向图G的两个不同的强联通分支。假设有一条边(u,v)属于E的反向图,其中u在C中,V在C’中,则F(C)<F(C’)。
第二次在反向图上DFS时,首先从完成时间最大的强联通分量C中的某个顶点x开始,访问C中的所有顶点,有推论可知,在反向图中,没有从C到任何其他连通分支的边,因而从x开始搜索不会搜到其他连通分支。
3、隐藏性质
如果我们把求出来的每个强连通分量收缩成一个点,并且用求出每个强连通分量的顺序来标记收缩后的节点,那么这个顺序其实就是强连通分量收缩成点后形成的有向无环图的拓扑序列。
二、 Trajan算法
1、伪代码:
Tarjan_Algorithm:
step1:
找一个没有被访问过的节点v,goto step2(v)。否则,算法结束。
step2(v):
初始化indx[v]和mlik[v]
对于v所有的邻接顶点u:
1) 如果没有访问过,则step2(u),同时维护mlik[v]
2) 如果访问过,但没有删除,维护mlik[v]
如果indx[v]==mlik[v],那么输出相应的强连通分量
2、原理
说明:indx[i]表示顶点i开始访问时间,mlik[i]为与顶点i邻接的顶点未删除顶点j的mlik[j]和mlik[i]的最小值。
强连通分量一定是图的深搜树的一个子树。
该算法与计算割点和桥的tarjan算法很像。
indx[v]==mlik[v],说明v的子孙能够通过一条未删除的边连到v,说v和其未删掉的子孙在一个强连通分量里。这时将这个强连通分量里的点和边都删除就可以继续求下一个强连通分量。
三、 Gabow算法
1、伪代码
Gabow_Algorithm:
step1:
找一个没有被访问过的节点v,goto step2(v)。否则,算法结束。
step2(v):
将v压入堆栈stk1[]和stk2[]
对于v所有的邻接顶点u:
1) 如果没有访问过,则step2(u)
2) 如果访问过,但没有删除,维护stk2[](处理环的过程)
如果stk2[]的顶元素==v,那么输出相应的强连通分量
2、原理:
这个算法其实就是Tarjan算法的变异体,它用第二个堆栈来辅助求出强连通分量的根,而不是Tarjan算法里面的indx[]和mlik[]数组。那么,我们说一下如何使用第二个堆栈来辅助求出强连通分量的根。
求出强连通分量的根:在Tarjan算法中,每次mlik[i]的修改都是由于环的出现(不然,mlik[i]的值不可能变小),每次出现环,在这个环里面只剩下一个mlik[i]没有被改变(深度最低的那个),或者全部被改变,因为那个深度最低的节点在另一个环内。那么Gabow算法中的第二堆栈变化就是删除构成环的节点,只剩深度最低的节点,或者全部删除,这个过程是通过出栈来实现,因为深度最低的那个顶点一定比前面的先访问,那么只要出栈一直到栈顶那个顶点的访问时间不大于深度最低的那个顶点。其中每个被弹出的节点属于同一个强连通分量。为什么弹出的都是同一个强连通分量?因为在这个节点访问之前,能够构成强连通分量的那些节点已经被弹出了,这个对Tarjan算法有了解的都应该清楚,那么Tarjan算法中的判断根我们用什么来代替呢?只须判断第二个堆栈的顶元素是不是当前顶点就可以了。