//将图G通过深度优先遍历,记录退出访问的序列(此序列为一逆拓扑序),并逐一入栈(拓扑序),将图G转置(即边的方向反转),对栈顶每个元素进行逐一深度遍历,遍历到的元素即为一个强连通分量;如果已经之前被遍历,则不再重复遍历。(这里使用邻接矩阵,可以使用十字链表来实现正逆矩阵)
//正确性证明,见网络
#include <iostream>
using namespace std;
const int MAXN = 110;
typedef int AdjTable[MAXN]; //邻接表类型
int n;
bool flag[MAXN]; //访问标志数组
int belg[MAXN]; //存储强连通分量,其中belg[i]表示顶点i属于第belg[i]个强连通分量
int numb[MAXN]; //结束时间标记,其中numb[i]表示离开时间为i的顶点
AdjTable adj[MAXN], radj[MAXN]; //邻接表,逆邻接表
//用于第一次深搜,求得numb[1..n]的值
void VisitOne(int cur, int &sig)
{
flag[cur] = true;
for ( int i=1; i<=adj[cur][0]; ++i )
{
if ( false==flag[adj[cur][i]] )
{
VisitOne(adj[cur][i],sig);
}
}
numb[++sig] = cur;
}
//用于第二次深搜,求得belg[1..n]的值
void VisitTwo(int cur, int sig)
{
flag[cur] = true;
belg[cur] = sig;
for ( int i=1; i<=radj[cur][0]; ++i )
{
if ( false==flag[radj[cur][i]] )
{
VisitTwo(radj[cur][i],sig);
}
}
}
//Kosaraju算法,返回为强连通分量个数
int Kosaraju_StronglyConnectedComponent()
{
int i, sig;
//第一次深搜
memset(flag+1,0,sizeof(bool)*n);
for ( sig=0,i=1; i<=n; ++i )
{
if ( false==flag[i] )
{
VisitOne(i,sig);
}
}
//第二次深搜
memset(flag+1,0,sizeof(bool)*n);
for ( sig=0,i=n; i>0; --i )
{
if ( false==flag[numb[i]] )
{
VisitTwo(numb[i],++sig);
}
}
return sig;
}
//正确性证明,见网络