1、强连通分量介绍:
在有向图G中,如果两个顶点vi,vj
间有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。
求解强连通分量的方法如下:
2、Kosaraju算法:
算法基于的事实: 逆图G^T
(同图中的每边的方向相反)和原图G
有着完全相同的连通分支.也就是说,如果顶点s
和t
在G
中是互达的,当且仅当s
和t
在G^T
中也是互达的。
2.1、算法步骤及示意图:
step1:对原图G进行深度优先遍历,记录每个节点的离开时间。
step2:选择具有最晚离开时间的顶点,对反图G^T
进行遍历,删除能够遍历到的顶点,这些顶点构成一个强连通分量。
step3:如果还有顶点没有删除,继续step2,否则算法结束。
图1:原始图G
图2:图G进行dfs
图3:逆图G^T
图4:逆图dfs,获得强连通分量==(逆图以最晚离开节点为起点进行DFS,遍历到的节点构成一个强连通分量)==
2.2、C++代码:
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=110;
int n; // 节点个数
bool flag[MAXN];//访问标志数组
int belg[MAXN];//存储强连通分量,其中belg[i]表示顶点i属于第belg[i]个强连通分量
int numb[MAXN];//结束时间标记,其中numb[i]表示离开时间为i的顶点
int G[MAXN][MAXN],GT[MAXN][MAXN]; //邻接矩阵,逆邻接矩阵
AdjTableadj[MAXN],radj[MAXN];//邻接表,逆邻接表
//用于第一次深搜,求得numb[1..n]的值
voidVisitOne(int cur,int &sig) // 访问原图
{
flag[cur]=true;
for(int i=0; i<n; ++i)
if(G[cur][i] && flag[i] == false)
VisitOne(i,sig);
numb[++sig]=cur; //其中numb[i]表示离开时间为i的顶点
}
//用于第二次深搜,求得belg[1..n]的值
voidVisitTwo(int cur,int count) //访问逆图
{
flag[cur]=true;
belg[cur]=count;
for(int i=0; i<n; ++i)
if(GT[cur][i] && flag[i] == false)
VisitOne(i,count);
}
//Kosaraju算法,返回为强连通分量个数
int Kosaraju_StronglyConnectedComponent()
{
int sig=0;// sig代表离开时间
//第一次深搜
memset(flag,0,sizeof(bool)*n); //访问标志设为false
for(int i=0; i<n; i++)
if( flag[i] == false)
VisitOne(i,sig);
//第二次深搜
memset(flag,0,sizeof(bool)*n); //访问标志设为false
int count=0; // count为强连通分量个数
for(int i=n; i>0; --i)
if(flag[numb[i]] == false) //频率最高的顶点若没访问
VisitTwo(numb[i],++count);
return count;
}
参考资料:
a、百度百科:kosaraju算法
b、强连通分量
c、算法导论22.5节 强连通分量
3、Tarjan算法与Gabow算法
这两个我还没学,有兴趣的见链接:百度百科:强连通分量
总结:
1、Kosaraju算法:逆图以最晚离开节点为起点进行DFS,遍历到的节点构成一个强连通分量。