Kosaraju算法:算法分两次dfs,第一次dfs会将所有的强连通分量(SCC)混淆在一起,按SCC图拓扑排序的逆序进行遍历即可将所有SCC分开。
#include<stdio.h> //tre_fei_ge
#include<string.h>
int stack[100],top;
int G2[12][12],G[12][12] = {{0,1,0,0,0,0,0,0,0,0,0,0},
{0,0,1,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,1,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,0,0,0,1,1,0,0,0,0,0},
{0,0,1,0,0,0,0,1,0,0,0,0},
{0,0,0,0,0,0,0,1,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,1},
{0,0,0,0,0,0,0,0,0,1,0,0}};//G2为图G的转置矩阵
int vis[12],sccno[12],scc_cnt;//vis访问标记数组,sccno点所在SCC标记数组
void dfs1(int u);
void dfs2(int u);
void find();
int main()
{
for(int j = 0;j < 12;j++)
for(int k = 0;k < 12;k++)
G2[k][j] = G[j][k];
memset(stack,-1,sizeof(stack));
top = 0;
memset(vis,0,sizeof(vis));
memset(sccno,0,sizeof(sccno));
scc_cnt = 0;
for(int i = 0; i < 12; i++) {
for(int j = 0; j < 12; j++)
printf("%d ",G[i][j]);
printf("\n");
}
printf("\n================================\n\n");
for(int i = 0; i < 12; i++) {
for(int j = 0; j < 12; j++)
printf("%d ",G2[i][j]);
printf("\n");
}
find();
for(int i = 0; i < 12; i++)
printf("%d\t",sccno[i]);
printf("\n");
for(int i = 0; i < 12; i++)
printf("%d\t",i);
return 0;
}
void dfs1(int u) {
if(vis[u]) return ;
vis[u] = 1;
for(int i = 0; i < 12; i++)
if(G[u][i])
dfs1(i);
stack[top++] = u;
}
void dfs2(int u) {
if(sccno[u]) return ;
sccno[u] = scc_cnt;
for(int i = 0; i < 12; i++)
if(G2[u][i])
dfs2(i);
}
void find() {
for(int i = 0; i < 12; i++) dfs1(i);
for(int i = 11; i >= 0; i--) {
if(!sccno[stack[i]]) {
scc_cnt++;
dfs2(stack[i]);
}
}
}
Tarjan算法:SCC的第一个线性算法,即有w->v->u,若v的后代能访问的最早的祖先为v及v是v所在的SCC的第一个被访问的点,若为w则w应为v所在的SCC的第一个被访问到的点,若为u则应先计算出u所在SCC的所有点的集合。
#include<stdio.h>
#include<string.h>
#define inited -1
int stack[100],top;
int G[12][12] = {{0,1,0,0,0,0,0,0,0,0,0,0},
{0,0,1,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,1,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,0,0,0,1,1,0,0,0,0,0},
{0,0,1,0,0,0,0,1,0,0,0,0},
{0,0,0,0,0,0,0,1,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,1},
{0,0,0,0,0,0,0,0,0,1,0,0}};
int pre[12],sccno[12],lowlink[12],scc_cnt,dfs_clock;//pre当前点访问时间标记数组,lowlink当前点所能到的最早祖先访问时间标记数组
void dfs(int u);
void init();
int min(int a,int b);
int main()
{
init();
for(int i = 0; i < 12; i++)
dfs(i);
for(int i = 0; i < 12; i++)
printf("%d\t",i);
printf("\n");
for(int i = 0; i < 12; i++)
printf("%d\t",sccno[i]);
return 0;
}
void init() {
memset(stack,-1,sizeof(stack));
top = -1;
memset(pre,-1,sizeof(pre));
memset(sccno,-1,sizeof(sccno));
memset(lowlink,-1,sizeof(lowlink));
scc_cnt = 0;
dfs_clock = 0;
for(int i = 0; i < 12; i++)
dfs(i);
}
void dfs(int u) {
pre[u] = lowlink[u] = ++dfs_clock;
stack[++top] = u;
for(int i = 0; i < 12; i++)
if(G[u][i] && pre[i] == inited) {
dfs(i);
lowlink[u] = min(lowlink[u],lowlink[i]);
} else if(G[u][i]){
lowlink[u] = min(lowlink[u],pre[i]);
}
printf("DFS %d %d\n",lowlink[u],pre[u]);
if(lowlink[u] == pre[u] && sccno[u] == inited) {
printf("======cul======\n");
scc_cnt++;
while(top >= 0) {
sccno[stack[top]] = scc_cnt;
if(u == stack[top--]) break;
}
}
}
int min(int a,int b) {
return a > b ? b : a;
}