求有向圖的強連通分量(c語言版)

jclih發表於2018-01-07

 有向圖G:


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也有路徑,即是通的。

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

原始碼地址:git@github.com: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語言寫的。第二次記錄點東西到csdn,如有不妥的地方,請幫忙指出。計算機的世界真的很精彩,我還需要努力的探索!加油,各位)

相關文章