一、定义
在有向图 G {G} G中,如果两个顶点 u , v {u,v} u,v间尊在一条 u {u} u到 v {v} v的路径且也存在一条 v {v} v到 u {u} u的路径,则称这两个顶点 u , v {u,v} u,v是强联通的(strongly
connected)。如果有向图 G {G} G是一个强连通图,有向非强连通图的极大强连通子图,称为强连通分量(strongly connected components)。
极大强连通子图: G {G} G是一个极大强连通子图,当且仅当 G {G} G是一个强连通子图且不存在另一个强连通子图 G ′ {G'} G′,使得 G {G} G是 G ′ {G'} G′的真子集。
举个例子:
在下图中,子图 { 1 , 2 , 3 , 4 } {\{1,2,3,4\}} {
1,2,3,4}是一个强连通分量,因为顶点 1 , 2 , 3 , 4 {1,2,3,4} 1,2,3,4两两可达, { 5 } , { 6 } {\{5\},\{6\}} {
5},{
6}也是两强连通分量。
应用:若将原图所有的强连通分量都缩为一个点,那么原图就会形成一个DAG(有向无环图)
二、Kosaraju算法
基于两次DFS的有向图强连通子图的算法。时间复杂度 O ( n + m ) {O(n+m)} O(n+m)
算法过程:
Ⅰ.对原有向图 G {G} G进行DFS,记录结点访问完的顺序 d [ i ] {d[i]} d[i], d [ i ] {d[i]} d[i]表示第 i {i} i个访问完的结点是 d [ i ] {d[i]} d[i]。
Ⅱ.选择具有最晚访问完的顶点,对反向图 G T {GT} GT进行DFS,删除能够遍历到的顶点,这些顶点构成一个强连通分量。
Ⅲ.如果还有顶点没有删除,继续第二步,否则算法结束。
举个栗子:
第一次遍历的访问次序是1,2,3,4;访问完成的顺序是3,2,1,4.。
第二次遍历是按照“访问完成的顺序”从后向前访问,所以第二次是先从4开始,然后是1,2,最后是3。
最后我们可以求出三个强连通分量: { ( 4 ) , ( 1 , 2 ) , ( 3 ) } {\{(4),(1,2),(3)\}} { (4),(1,2),(3)}。
算法模板:
#include<bits/stdc++.h>
using namespace std;
const int Max = 101;
int vis[Max]={
0},mapp[Max][Max],d[Max],n,t=0;
void dfsone(int x) {
vis[x] = 1;
for(int i = 1; i <= n; i++) {
if(!vis[i] && mapp[x][i]) dfsone(i);
}
d[++t] = x;
}
void dfstwo(int x) {
vis[x] = t;
for(int i = 1; i <= n; i++)
if(!vis[i] && mapp[i][x]) dfstwo(i);
}
void Kosaraju() {
int i;
t = 0;
//进行第一次DFS遍历
for(i = 1; i<= n; i++)
if(!vis[i]) dfsone(i);
memset(vis,0,sizeof(vis));
t = 0;
printf("\nd[i]:\n"