有向图的强连通分支算法kosaraju(C语言实现)

有向图G中,如果两个顶点vi,vj间有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的任意两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图(即:一个图的(强)连通子图,并且加入任何一个不在它的点集中的点都会导致它不再(强)连通。),称为强连通分量(strongly connected components)。如图:


红色圈起来的部分为该有向图的3个强连通分量。

求一个有向图的强连通分量有Kosaraju算法Tarjan算法Gabow算法。在Tarjan 算法和 Gabow 算法的过程中,只需要进行一次的深度优先搜索,而Kosaraju算法需要两次DFS,因而相对 Kosaraju 算法较有效率。这些算法可简称为SSC(strongly connected components)算法;但是kosaraju算法容易理解,也比较通用。本文只介绍kosaraju算法。其算法思想是:

step1:对原图G进行深度优先遍历,记录每个节点的离开时间。
step2:选择具有最晚离开时间的顶点,对逆图GT进行遍历,删除能够遍历到的顶点,这些顶点构成一个强连通分量。
step3:如果还有顶点没有删除,继续step2,否则算法结束。

理解这个算法需要理解强连通分量,以及理解逆图GT和原图G有着完全相同的连通分支,也就说,如果顶点s和t在G中是互达的,当且仅当s和t在GT中也是互达的。下面是C语言实现,用堆栈的先进后出的特性实现记录每个节点的离开时间。

/* Kosaraju求强连通分量邻接矩阵 */
#include "stdio.h"
#include "stdlib.h"
#define Vexnum 100
int map1[Vexnum][Vexnum]={0};
int nmap[Vexnum][Vexnum]={0};
int visited[Vexnum];

/*这部分是栈的定义和操作,可直接使用*/
struct StackSq{
int *stack1;
int top;
int MaxSize;
};
struct StackSq s;
void InitStack(struct StackSq *s)
{
s->MaxSize=100;/*置栈空间初始最大长度为100*/
s->stack1=(int *)malloc(10*sizeof(int));/*动态存储空间分配*/
s->top=-1;/*置栈为空*/
}
//1
void Push(struct StackSq* S,int x)
{
// if(S->top==S->MaxSize-1)againMalloc(S);/*若栈空间用完则重新分配更大的存储空间*/
S->top++;/*栈顶指针后移一个位置*/
S->stack1[S->top]=x;/*将新元素插入到栈顶*/
}
//2
int Pop(struct StackSq* S)
{
if(S->top==-1){
printf("栈空,无元素出栈!\n");
exit(1);
}
S->top--;
return S->stack1[S->top+1];
}
//3
int Peek(struct StackSq* s)
{
if(s->top==-1){
printf("栈空,无元素出栈!\n");
exit(1);
}
return s->stack1[s->top];
}
int EmptyStack(struct StackSq* S)
{
if(S->top==-1) return 1;
else return 0;
}

int N;  /*顶点数目*/
int DFS1(int  v) /* 深度遍历有向图G*/
{
    visited[v] = 1;
    for (int i = 1;i <= N;i++)
        if (!visited[i] && map1[v][i])
            DFS1( i );
    Push(&s,v);    /*这步很妙,时间顺序*/
    return 0;
}
int DFS2( int v ) /* 深度遍历有向图G的逆图*/
{
    visited[v] = 1;
    printf( "%d,", v );
    for (int i = 1;i <= N;i++)
        if ( !visited[i] && nmap[v][i] )
            DFS2( i );
    return 0;
}
int kosaraju()
{
    int i;
    while (!EmptyStack(&s))
        Pop(&s);
    for(i=1;i<=N;i++)
        visited[i]=0;
    for (int i = 1;i <= N;i++)
        if (!visited[i]) DFS1( i );
    int t = 0;
     for(i=1;i<=N;i++)
        visited[i]=0;
    while (!EmptyStack(&s))
    {
        int v = Peek(&s);
        Pop(&s);
        printf("{");
        if (!visited[v])
        {
            t++;
            DFS2( v );
        }
        printf("}");
    }
    return t;
}

/*测试*/
int main()
{
    int M,v,e;
    InitStack(&s);
    scanf("%d%d",&N,&M);  /*输入有向图的顶点数和边数*/
    for (int i = 0; i < M; i++)
    {
        scanf("%d%d",&v,&e);
        map1[v][e] = 1;   /* 使用邻接矩阵保留图的信息*/
        nmap[e][v] = 1;     /* 使用邻接矩阵保留逆图的信息*/
    }
    printf("%d\n", kosaraju()); /* 输出连通分量个数 */
    return 0;
}

程序输入 4 5  /*4个顶点,5条边,顶点从1开始编号*/

1 2

2 3

4 3

1 4

4  1

输出

{1,4}{}{2,}{3,}3  /*3个强连通分量{1,4},{2},{3}。

输出格式没弄好,自己还可用改下。   


  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值