Kosaraju算法求有向图的极大连通子图。算法首先对逆图做后序遍历,求出后序编码。随后从右到左扫描编码,对于每一个未标记所属分量编号的节点,以它为根做一次DFS,标记沿途所遇到的所有节点为同一个强连通分量。
算法正确性的证明基于:任意两点s,t在原图中属于强联通分量的话,当且仅当在逆图中两者也属于同一强连通分量
#define MAXV 100000
static int postR[MAXV],post[MAXV];
static int cnt0,cnt1;
typedef struct LinkNod *Link;
struct LinkNod{
int dest;
Link next;
};
typedef struct GraphNod *Graph;
struct GraphNod{
Link v[MAXV];
int vnum;
int id[MAXV]; // 分量标号
};
int IsScc(Graph g,int s,int t){
return g->id[s] == g->id[t];
}
void DFS(Graph g,int w){
g->id[w] = cnt1;
for( Link pos = g->v[w]; pos!=NULL; pos=pos->next ) if( g->id[ pos->dest ] == -1 ) DFS(g,pos->dest);
post[cnt0++] = w; // 后序编码
}
Graph MakeGraphR(Graph g){
Link pos;
Link tmpNod;
Graph r = (Graph)malloc(sizeof(struct GraphNod));
r->vnum = g->vnum;
for(int i=0;i<r->vnum;i++) r->v[i] = NULL;
for(int i=0;i<g->vnum;i++){
pos = g->v[i];
while(pos){
tmpNod = (Link)malloc(sizeof(struct LinkNod));
tmpNod->dest = i; tmpNod->next = r->v[pos->dest];
r->v[ pos->dest ] = tmpNod;
pos=pos->next;
}
}
return r;
}
int FindScc(Graph g){
Graph r = MakeGraphR(g);
memset(r->id,-1,sizeof(r->id));
cnt0 = cnt1 =0;
for( int i=0;i<r->vnum;i++ ) if( r->id[i] == -1 ) DFS(r,i);
for(int i=0;i<g->vnum;i++) postR[i] = post[i];
memset(g->id,-1,sizeof(g->id));
cnt0 = cnt1 =0;
for( int i=g->vnum-1;i>-1;i-- ) if( g->id[ postR[i] ] == -1 ){ DFS(g,postR[i]); cnt1++; }
printf("分量阵:\n");
for( int i=0;i<g->vnum;i++ ) printf("%d: %d\n",i,g->id[i]);
return cnt1; // 分量数
}