求有向图的强连通分量(c语言版)

 有向图G:

求有向图的强连通分量(c语言版)

1.选用何种结构存储有向图?

选用十字链表的结构来存储图

2.选用何种遍历方式?

选用深度优先搜索

3.思路

3.1 首先从图G上某个顶点出发沿以该顶点为尾的弧深度优先搜索,在顶点深度优先搜索结束之时把该顶点存放到辅助数组finished中(程序中实际存放的是该顶点在图中的位置,当然顶点的位置是人为定的,a的位置是0,b的位置是1,c的位置是2,d的位置是3,顶点在图的结构体中用数组存起来)。

  解释:假如从a顶点开始深度遍历,过程是a->c->d,那么finished数组的值是 {3,2,0},深度遍历完后,a顶点的访问才结束,所以a最后退出这次遍历,a存放到finished数组最后。

3.2 然后从最后搜索完成的顶点(即finished数组中最后一个元素)出发,沿着以该顶点为头的弧作逆向的深度优先搜索遍历,若以该顶点的这次深度遍历不能访问到图中所有的顶点,就从数组finished中最后完成搜索的顶点(还未被访问的)再次深度优先搜索遍历,以此类推,直到图中所有的顶点都被访问过。

 解释:假如finished数组中的值是{1,3,2,0},从0开始逆向的深度优先搜索,能访问到2,3,但是1访问不到,假如1前面还有4,5顶点({4,5,1}),因为数组finished是按退出深度优先搜索的顺序存进去的,所以1就是当前最后完成深度优先搜索的顶点(相比4,5顶点),因此再从1顶点开始逆向深度优先遍历,以此类推,直到所有顶点都访问到。

3.3 打印出来的结果就是各强连通分量的顶点集。

4.原理

强连通分量的意思是 有向图G=(V,{A}),(V代表顶点集,A代表弧),如果对于每一对vi,vj属于V,vi不等于vj,从vi到vj和从vj到vi都存储路径,称G是强连通图,而有向图中的极大连通子图叫有向图的强连通分量(如vi到vj有路径,vj到vi也要有路径,才叫强连通)。根据图,从a顶点根据以弧尾是a的弧能访问到d,那么从a顶点根据以弧头是a的弧也能访问到d,说明a到d有路径,d到a也有路径,即是通的。

------------------------------------------------------------------------------------------------------------

源码地址:[email protected]:hglspace/OlGraph.git

-------------------------------------------------------------------------------------------------------------

myhead.c

#define MAX_VERTEX_NUM 4//人为设置顶点数为4

typedef int Bool;//自定义布尔型变量

#define True 1//以访问状态

#define False 0//未访问状态

//十字链表存储有向图

struct ArcBox{//弧结点

    int tailvex;//尾结点在图中的位置

    int headvex;//头结点在图中的位置

    struct ArcBox * hlink;//弧头相同的下一条弧

    struct ArcBox * tlink;//弧尾相同的下一条弧

    Bool mark;//访问的标志 True代表访问过,False代表没有访问过

};

struct VexNode{//顶点结点

    char data;//顶点信息

    struct ArcBox * firstin;//以该顶点为弧头的第一条弧

    struct ArcBox * firstout;//以该顶点为弧尾的第一条弧

};

struct OlGraph{

    struct VexNode xlist[MAX_VERTEX_NUM];//顶点结点数组

    int vexnum;//当前顶点数量

    int arcnum;//当前弧的数量

};

//自定义头文件

-----------------------------------------------------------

operateGraph.c



#include <stdio.h>

#include "myhead.h"

#include <stdlib.h>

Bool visits[MAX_VERTEX_NUM];//定义顶点辅助数组,记录顶点是否访问过

int count=0;//定义全局变量,辅助数组finished使用

struct ArcBox * edg[MAX_VERTEX_NUM];//定义全局弧指针数组,记录弧地址,方便把弧的是否访问过的状态重置

/*

   初始化有向图

 */

struct OlGraph init(void){

    int i,j,k,m;

    char data1,data2;//用来接受顶点的信息,根据信息查找顶点的位置

    struct OlGraph g;

    int locateVex(struct OlGraph g,char data);//声明查找顶点位置的函数

    printf("请输入图的顶点数:");

    scanf("%d",&g.vexnum);

    printf("请输入图的边数:");

    scanf("%d",&g.arcnum);

    //开始初始化顶点和弧

    for(i=0;i<g.vexnum;i++){//初始化顶点

        printf("请输入第%d个顶点:",i+1);

        scanf(" %c",&g.xlist[i].data);

        g.xlist[i].firstin=NULL;

        g.xlist[i].firstout=NULL;

        visits[i]=False;

    }

    for(j=0;j<g.arcnum;j++){//初始化弧

        struct ArcBox * p = malloc(sizeof(struct ArcBox));//开辟存储弧的空间

        if(p==NULL){//如果开辟失败,程序终止

           exit(-1);

                }

        printf("请输入第%d条边的起点:",j+1);

        scanf(" %c",&data1);

        printf("请输入第%d条边的终点:",j+1);

        scanf(" %c",&data2);

        k=locateVex(g,data1);

        m=locateVex(g,data2);

        p->headvex=m;//对弧的头结点赋值

        p->tailvex=k;//对弧的尾结点赋值

        p->hlink=NULL;

        p->tlink=NULL;

        p->mark=False;//弧的状态置为未访问状态

        if(g.xlist[m].firstin==NULL){//m结点是头结点,需要动态改变该结点第一个以该结点为头结点的弧

            g.xlist[m].firstin=p;

        }else{

            p->hlink=g.xlist[m].firstin;

            g.xlist[m].firstin=p;

        }

        if(g.xlist[k].firstout==NULL){//k结点是尾结点,需要动态改变该结点第一个以该结点为尾结点的弧

            g.xlist[k].firstout=p;

        }else{

            p->tlink=g.xlist[k].firstout;

            g.xlist[k].firstout=p;

        }

        edg[j]=p;//把弧的指针存储到 弧指针数组中

    }

    return g;

}


/*

  根据顶点的信息查找顶点在图中的位置

 */

int locateVex(struct OlGraph g,char data){

    int i;

    for(i=0;data!=g.xlist[i].data;i++);

    return i;

}


/*

 生成有向图的强连通分量的顶点集

 */

void DFSTraverse(struct OlGraph g){

    int i,k,s;

    int finished[MAX_VERTEX_NUM];//该数组存储退出DFS函数的顶点(按照退出的顺序,先退出来的顶点先记录。记录的是顶点的位置),作用是:方便反向深度遍历顶点

    void DFS(struct OlGraph g,int i,int finished[]);//声明深度遍历顶点的函数

    void DFSRever(struct OlGraph g,int i,int finished[]);//声明按照数组finished中存储的顶点,从finished[n-1]元素中存储的顶点开始深度遍历的函数 (n是存储的顶点个数)

    for(i=0;i<g.vexnum;i++){//开始遍历顶点

        if(visits[i]==True){//如果访问过,就跳过

            continue;

        }

        DFS(g,i,finished);//调用函数

        //finished[count++]=i;

    }

    for(k=0;k<g.vexnum;k++){//把顶点访问的标志重置到开始状态

        visits[k]=False;

    }

    for(s=0;s<g.arcnum;s++){//把弧的访问标志重置到开始状态

        edg[s]->mark=False;

    }

    while(count>=1){//按照finished数组中存放的顶点,开始深度遍历

        if(visits[finished[count-1]]==True){//如果该顶点访问过就跳过

            continue;

        }

        visits[finished[count-1]]=True;//置该顶点的访问状态为已访问

        printf("%c",g.xlist[finished[count-1]].data);//打印该顶点的信息,其实就是对该顶点进行操作,访问

        DFSRever(g,finished[count-1],finished);//调用函数遍历

        printf("\n.........\n");

        count--;//访问下一顶点

    }

}

void DFSRever(struct OlGraph g,int i,int finished[]){

    int j;

    int getTailAdjVex(struct OlGraph g,int i);//声明查找以该顶点为头的弧的尾顶点的函数

    for(j=getTailAdjVex(g, i);j>=0;j=getTailAdjVex(g, i)){

        if(visits[j]==True){

            continue;

        }

        visits[j]=True;

        DFSRever(g,j,finished);//递归调用,查找i结点的邻接点的邻接点

        printf("%c",g.xlist[j].data);//访问i结点的邻接点

    }

}

void DFS(struct OlGraph g,int i,int finished[]){

    visits[i]=True;

    int w;

    int getHeadAdjVex(struct OlGraph g,int i);//声明以该结点为尾的弧的头结点的函数

    for(w=getHeadAdjVex(g,i);w>=0;w=getHeadAdjVex(g,i)){

        if(visits[w]==True){

            continue;

        }

        DFS(g,w,finished);//递归调用,其实就是深度遍历

    }

    finished[count++]=i;//退出函数时把该结点存储到finished数组中

}


/*

 查找以该结点为尾的弧的头结点

 */

int getHeadAdjVex(struct OlGraph g,int i){

    struct ArcBox * p=g.xlist[i].firstout;

    if(p==NULL){

        return -1;

    }

    //for(p=g.xlist[i].firstout;p->mark==True;p=p->tlink);

    while(p->mark==True){

        p=p->tlink;

        if(p==NULL){

            return -1;

        }

    }

    p->mark=True;

    return p->headvex;

  

}


/*

 查找以该顶点为头的弧的尾顶点

 */

int getTailAdjVex(struct OlGraph g,int i){

    struct ArcBox * p=g.xlist[i].firstin;

    if(p==NULL){

        return -1;

    }

    //for(;p->mark==True;p=p->hlink);

    while(p->mark==True){

        p=p->hlink;

        if(p==NULL){

            return -1;

        }

    }

    p->mark=True;

    return p->tailvex;

}


-----------------------------------------------------------------------

main.c

#include <stdio.h>

#include "myhead.h"

int main(int argc, const char * argv[]){

    //printf("你好");

    struct OlGraph init(void);

    void DFSTraverse(struct OlGraph g);

    struct OlGraph g=init();

    DFSTraverse(g);

    return 0;

}

 (结语:我主要是做java开发的,为什么用c语言来写关于有向图的操作呢?主要是大学里学过c,懂一点基础,为了能更深入了解c语言,业余时间就用c写点程序练习练习。个人感觉学好c语言有助于理解更高级的如java语言,java的虚拟机就是有用c语言写的。第二次记录点东西到****,如有不妥的地方,请帮忙指出。计算机的世界真的很精彩,我还需要努力的探索!加油,各位)