求有向图强连通分量个数

来源:互联网 发布:java转换字符串编码 编辑:程序博客网 时间:2024/05/16 13:32

https://www.byvoid.com/blog/scc-tarjan/

强连通图(Strongly Connected Graph)是指一个有向图(Directed Graph)中任意两点v1、v2间存在v1到v2的路径(path)及v2到v1的路径的图。

在一个有向图中,其强连通子图(大到不能再大,就是相当于扩张到最大,如强连通子图的强连通子图不算个数)就是他的强连通分量,现在,我们要做的就是求出一个有向图中,他的强连通分量的个数。

1、Kosaraju算法 [邻接矩阵]

该算法可用来计算强连通分量的个数,并收缩强连通分量。

这个算法是最容易理解、最通用的算法,其比较关键的部分是同时应用了原图G和反图G‘。

步骤如下:

one:对原图进行DFS并将出栈顺序进行逆序,得到的顺序就是拓扑顺序;

two:将原图每条边进行反向;

three:按照one中生成顺序在进行DFS染色,染成同色的就是同一个强连通块。

该算法具有一个隐藏性质:如果我们把求出来的每个强连通分量收缩成一个点,并且用每个强连通分量的顺序来标记收缩后的结点,那么这个顺序其实就是强连通分量收缩后形成的有向无环图的拓扑序列。

int G[10][10];
int dfn[10],top;
int color[10],cnt;
void dfs1(int k){
color[k]=1;
for(int i=1;i<=n;i++){
if(G[k][i]!=0&&!color[i])
dfs1(i);
}
dfn[top++]=k;
}
void dfs2(int k){
color[k]=cnt;
for(int i=1;i<=n;i++){
if(G[i][k]!=0&&!color[i])
dfs2(i);
}
}
int main()
{
    top=cnt=0;
for(int i=1;i<=n;i++)
if(!color[i])dfs1(i);
memset(color,0,sizeof(color));
for(int i=n;i>=1;i--){
if(!color[dfn[i]]){
cnt++;
dfs2(dfn[i]);
}
}
cout<<cnt;
    return 0;
}


2、Tarjan算法[邻接表]

int  flag[N];//flag[i]==0表示还没有访问过;1表示访问过,但还没有去掉;-1表示已经去掉了。

int color[N];//color[i]表示定点i所属的强连通分量。

int stack[N];//堆栈,辅助作用。

int low[N];//(很关键)表示与其邻接但还未删除定点的最小访问时间。

int index[N];//顶点的访问时间(像一个标准值,对照用的)

void dfs(int x,int &sig,int &count)    //深搜过程,算法的主要

    stack[++top]=x;

     flag[i]=1;

     low[i]=index[i]=++sig;    //初始化

    for(edge *e=adj[x];e!=NULL;e=e->next){

      int v=e->v;

       if(flag[v]==0){

       dfs(v,sig,count );

       if(low[v]<low[x])low[x]=low[v];

     }else if(flag[x]==1&&index[v]<low[x]){low[x]=index[v];}

     }

   if(low[x]==index[x]){

   count++;

   int t;

   do{

       t=stack[top--];

       color[t]=count;

       flag[t]=-1;

     }while(t!=x)

    }

int Tarjan()

{

  int sig,count;

memset(flag,0,sizeof(flag) );

sig=count=top=0;

for(int i=1;i<=n;i++)if(flag[i]==0)dfs(i,sig,count);

return count;

}

3、Gabow算法[邻接表]


0 0