小朋友學數據結構(16):基於鄰接矩陣的的深度優先遍歷和廣度優先遍歷

觀察下面兩個無向圖:

1.png

這兩個圖其實是一樣的,只是畫法不同罷了。第一張圖更有立體感,第二張圖更有層次感,並且把A點置爲頂點(事實上圖的任何一點都可以做爲頂點)。

一、用數組來存放頂點

vexs[0] = ‘A’
vexs[1] = ‘B’
vexs[2] = ‘C’
vexs[3] = ‘D’
vexs[4] = ‘E’
vexs[5] = ‘F’
vexs[6] = ‘G’
vexs[7] = ‘H’
vexs[8] = ‘I’

二、用鄰接矩陣來表示邊

2.png

上面這個矩陣中,0表示每個頂點沒有到達自己的路徑。1表示兩個頂點之間有路徑,無窮大表示兩個頂點之間沒有路徑。 假如按照程序計數習慣,行或列都從0數起。 第0行第0列爲0,表示A到它本身之間沒有路徑(這是人爲規定的,因爲A到它自身不需要路徑)。 第0行第1列爲1,表示頂點A和B之間有路徑。 第0行第5列爲1,表示頂點A和頂點F之間有路徑。 第0行其他列爲無窮大,表示A到其它點之間沒有路徑。 …… 因爲是無向圖,鄰接矩陣必然有兩個特點: ① 對角線(左上角到右下角)上的元素值全爲0.表示每個點到它自身沒有(或不需要)路徑。 ② 其它的元素關於對角線對稱。 上面的鄰接矩陣,在編程時可以用二維數組來實現:

 arc[0][1] = arc[1][0] = 1;
 arc[0][5] = arc[5][0] =1;

 arc[1][2] = arc[2][1] = 1;
 arc[1][6] = arc[6][1] = 1;
 arc[1][8] = arc[8][1] = 1;

 arc[2][3] = arc[3][2] = 1;
 arc[2][8] = arc[8][2] = 1;

 arc[3][4] = arc[4][3] = 1;
 arc[3][6] = arc[6][3] = 1;
 arc[3][7] = arc[7][3] = 1;
 arc[3][8] = arc[8][3] = 1;

 arc[4][5] = arc[5][4] = 1;
 arc[4][7] = arc[7][4] = 1;

 arc[5][6] = arc[6][5] = 1;

 arc[6][7] = arc[7][6] = 1;

三、深度優先遍歷

可以使用遞歸的方法進行深度遍歷。 觀察圖(1)中的左圖,假如從頂點A開始,從A找到相鄰的B,從B找到相鄰的C,從C找到相鄰的D,從D找到相鄰的E,從E找到鄰接點F,從F找到相鄰的G,從G找到相鄰的H。 H有三個相鄰的點D、E、G。這三個點都已經遍歷過了。所以回退到上一頂點G。 G有三個相鄰的頂點D、F、H。這三個點也都已經遍歷過了。回退到上一頂點F。 F有兩個相鄰的頂點E、G,都已經遍歷過了。回退到上個頂點E。 E點有兩個相鄰的頂點D、F,都已經遍歷過了。回退到上個頂點D。 D點有五個相鄰的頂點C、E、H、G、I。除了I外,其餘四個頂點已經遍歷過了。所以這一次遍歷I。 I遍歷完之後回到D點,從D點回到C點。 C點有三個相鄰的頂點B、D、I,都已經遍歷過了,回退到B點。 B點有四個相鄰的頂點A、C、G、I,都已經遍歷過了,回退到A點。 A點有兩個相鄰的頂點B、F,都已經遍歷過了。遞歸都此結束。 得到深度優先遍歷的順序爲:A B C D E F G H I

四、廣度優先遍歷

廣度優先遍歷需要藉助於另外的數據結構隊列。當把圖中的頂點放到隊列中時,表示這個頂點被遍歷了(可以把頂點的值打印出來)。 用圖1中的右圖來分析廣度優先遍歷更方便,因爲右圖的層次結構更明顯。

3.png

起初,把點A放入隊列中,A被遍歷。如上圖中的(1)所示。 接着把隊首元素A出隊,把A的下一層的頂點B和F移進隊列,B和F被遍歷。如上圖中的(2)所示。 隊首元素B出隊,B的下一層頂點C,G,I相繼入隊,C、G和I被遍歷。如上圖中的(3)所示。 隊首元素F出隊,F的下一層頂點E入隊,E被遍歷。如上圖中的(5)所示。 隊首元素C出隊,C的下一層頂點D入隊,D被遍歷。如上圖中的(6)所示。 隊首元素G出隊,G的下一層有兩個頂點:D和H。D已在隊列裏,H入隊,H被遍歷。如上圖中的(4)所示。 隊首元素I出隊,I的下一層頂點D已在隊列裏,沒有新頂點入隊。如上圖中的(7)所示。 隊首元素E出隊,E的下一層頂點D和H都已在隊列裏,沒有新頂點入隊。如上圖中的(8)所示。 隊首元素D出隊,D沒有下一層頂點,所以沒有新頂點入隊。如上圖中的(9)所示。 隊首元素H出隊,H沒有下一層頂點,所以沒有新頂點入隊。此時隊列爲空,遍歷結束。 最終,廣度優先遍歷的順序即入隊列(或出隊列)的順序:A B F C G I E D H

五、完整代碼

#include "stdio.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 9 /*  存儲空間初始分配量 */
#define MAXEDGE 15
#define MAXVEX 9
#define INFINITY 65535


typedef int Status;  /* Status 是函數的類型,其值是函數結果狀態代碼,如 OK 等 */
typedef int Boolean; /* Boolean 是布爾類型,其值是 TRUE 或 FALSE */
typedef char VertexType; /*  頂點類型應由用戶定義 */
typedef int EdgeType; /*  邊上的權值類型應由用戶定義 */


typedef struct
{
    VertexType vexs[MAXVEX]; /*  頂點表 */
    EdgeType arc[MAXVEX][MAXVEX];/*  鄰接矩陣,可看作邊表 */
    int numVertexes, numEdges; /*  圖中當前的頂點數和邊數 */
}MGraph;


/*  用到的隊列結構與函數********************************** */
/*  循環隊列的順序存儲結構 */
typedef struct
{
    int data[MAXSIZE];
    int front;    /* 頭位置標識,相當於頭指針 */
    int rear;    /* 尾位置標識,相當於尾指針。若隊列不爲空,指向隊列尾元素的下一個位置 */
}Queue;


/*  初始化一個空隊列 Q */
Status InitQueue(Queue *Q)
{
    Q->front=0;
    Q->rear=0;

    return    OK;
}


/*  若隊列 Q 爲空隊列,則返回 TRUE,否則返回 FALSE */
Status QueueEmpty(Queue Q)
{
    if(Q.front==Q.rear) /*  隊列空的標誌 */
        return TRUE;
    else
        return FALSE;
}


/*  若隊列未滿,則插入元素 e 爲 Q 新的隊尾元素 */
Status EnQueue(Queue *Q,int e)
{
    if ((Q->rear+1)%MAXSIZE == Q->front)  /*  隊列滿的判斷 */
        return ERROR;

    Q->data[Q->rear]=e;    /*  將元素 e 賦值給隊尾 */
    Q->rear=(Q->rear+1)%MAXSIZE;/* rear 指針向後移一位置,若到最後則轉到數組頭部 */

    return    OK;
}


/*  若隊列不爲空,則刪除 Q 中隊頭元素,用e返回其值 */
Status DeQueue(Queue *Q,int *e)
{
    if (Q->front == Q->rear)    /*  隊列空的判斷 */
        return ERROR;

    *e=Q->data[Q->front];    /*  將隊頭元素賦值給 e */
    Q->front=(Q->front+1)%MAXSIZE; /* front 指針向後移一位置,若到最後則轉到數組頭部 */

    return    OK;
}


void CreateMGraph(MGraph *G)
{
    int i, j;

    G->numEdges=15;
    G->numVertexes=9;

    /*  讀入頂點信息,建立頂點表 */
    G->vexs[0]='A';
    G->vexs[1]='B';
    G->vexs[2]='C';
    G->vexs[3]='D';
    G->vexs[4]='E';
    G->vexs[5]='F';
    G->vexs[6]='G';
    G->vexs[7]='H';
    G->vexs[8]='I';

    for (i = 0; i < G->numVertexes; i++)/*  初始化圖 */
    {
        for ( j = 0; j < G->numVertexes; j++)
        {
            G->arc[i][j]=0;
        }
    }

    G->arc[0][1]=1;
    G->arc[0][5]=1;

    G->arc[1][2]=1;
    G->arc[1][6]=1;
    G->arc[1][8]=1;

    G->arc[2][3]=1;
    G->arc[2][8]=1;

    G->arc[3][4]=1;
    G->arc[3][6]=1;
    G->arc[3][7]=1;
    G->arc[3][8]=1;

    G->arc[4][5]=1;
    G->arc[4][7]=1;

    G->arc[5][6]=1;

    G->arc[6][7]=1;

    for(i = 0; i < G->numVertexes; i++)
    {
        for(j = i; j < G->numVertexes; j++)
        {
            G->arc[j][i] =G->arc[i][j];
        }
    }
}

Boolean visited[MAXVEX]; /*  訪問標誌的數組 */

/*  鄰接矩陣的深度優先遞歸算法 */
void DFS(MGraph G, int i)
{
    int j;
    visited[i] = TRUE;
    printf("%c ", G.vexs[i]);/*  打印頂點,也可以其它操作 */

    for(j = 0; j < G.numVertexes; j++)
        if(G.arc[i][j] == 1 && !visited[j])
            DFS(G, j);/*  對未訪問的鄰接頂點遞歸調用 */
}


/*  鄰接矩陣的深度遍歷算法 */
void DFSTraverse(MGraph G)
{
    int i;
    for(i = 0; i < G.numVertexes; i++)
        visited[i] = FALSE; /*  初始所有頂點狀態都是未訪問過狀態 */

    for(i = 0; i < G.numVertexes; i++)
        if(!visited[i]) /*  對未訪問過的頂點調用 DFS,若是連通圖,只會執行一次 */
            DFS(G, i);
}


/*  鄰接矩陣的廣度遍歷算法 */
void BFSTraverse(MGraph G)
{
    int i, j;
    Queue Q;

    for(i = 0; i < G.numVertexes; i++)
        visited[i] = FALSE;

    InitQueue(&Q);    /*  初始化一輔助用的隊列 */

    for(i = 0; i < G.numVertexes; i++)    /*  對每一個頂點做循環 */
    {
        if (!visited[i])   /*  若是未訪問過就處理 */
        {
            visited[i]=TRUE;    /*  設置當前頂點訪問過 */
            printf("%c ", G.vexs[i]);/*  打印頂點,也可以其它操作 */
            EnQueue(&Q,i);    /*  將此頂點入隊列 */

            while(!QueueEmpty(Q)) /*  若當前隊列不爲空 */
            {
                DeQueue(&Q,&i);   /*  將隊對元素出隊列,賦值給 i */
                for(j=0;j<G.numVertexes;j++)
                {
                    /*  判斷其它頂點若與當前頂點存在邊且未訪問過    */
                    if(G.arc[i][j] == 1 && !visited[j])
                    {
                        visited[j]=TRUE;    /*  將找到的此頂點標記爲已訪問*/
                        printf("%c ", G.vexs[j]);   /*  打印頂點 */
                        EnQueue(&Q,j);    /*  將找到的此頂點入隊列  */
                    }
                }
            }
        }
    }
}


int main(void)
{
    MGraph G;
    CreateMGraph(&G);

    printf("\n 深度遍歷:");
    DFSTraverse(G);

    printf("\n 廣度遍歷:");
    BFSTraverse(G);

    return 0;
}

運行結果:

深度遍歷:A B C D E F G H I
廣度遍歷:A B F C G I E D H

瞭解小朋友學編程請加QQ307591841(微信與QQ同號),或QQ羣581357582。 關注公衆號請掃描二維碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章