廣度優先遍歷


觀察下面兩個無向圖:

 

 

 

這兩個圖其實是一樣的,只是畫法不同罷了。第一張圖更有立體感,第二張圖更有層次感,並且把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’

二、用鄰接矩陣來表示邊

 

 

 

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

如果不同的頂點之間沒有路徑,也是用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被遍歷。如上圖中的(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 MAXVEX 9 #define MAXSIZE 9 typedef int Status; /* Status 是函數的類型,其值是函數結果狀態代碼,如 OK 等 */ typedef int Boolean; /* Boolean 是布爾類型,其值是 TRUE 或 FALSE */ typedef int EdgeType; /* 邊上的權值類型應由用戶定義 */ // 圖的結構 struct Graph { char vexs[MAXVEX]; // 頂點,字符類型,比如頂點'A',頂點'B'... int arc[MAXVEX][MAXVEX];// 邊或路徑,0表示兩個頂點之間沒有路徑,1表示兩個頂點之間有路徑 int vexNum; // 頂點數量 int arcNum; // 邊(路徑)的數量 }; /* 用到的隊列結構與函數********************************** */ /* 循環隊列的順序存儲結構 */ typedef struct { int data[MAXSIZE]; int front; /* 頭位置標識,相當於頭指針 */ int rear; /* 尾位置標識,相當於尾指針。若隊列不為空,指向隊列尾元素的下一個位置 */ }Queue; /* 初始化一個空隊列 Q */ void InitQueue(Queue *Q) { Q->front = 0; Q->rear = 0; } /*若隊列 Q 為空隊列,則返回 TRUE,否則返回 FALSE 這里只判斷隊列是否為空,而不改變隊列的結構,所以參數不需用指針*/ Status QueueEmpty(Queue Q) { if(Q.front == Q.rear) /* 隊列空的標志 */ { return TRUE; } 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 CreateGraph(Graph *G) { G->arcNum = 15; G->vexNum = 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'; int i, j; for(i = 0; i < G->vexNum; i++)/* 初始化圖 */ { for (j = 0; j < G->vexNum; 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->vexNum; i++) { for(j = i; j < G->vexNum; j++) { G->arc[j][i] = G->arc[i][j]; } } } Boolean visited[MAXVEX]; /* 訪問標志的數組 */ /* 鄰接矩陣的廣度遍歷算法 */ void BFSTraverse(Graph G) { int i, j; Queue Q; for(i = 0; i < G.vexNum; i++) { visited[i] = FALSE; } InitQueue(&Q); /* 初始化一輔助用的隊列 */ for(i = 0; i < G.vexNum; 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.vexNum; j++) { /* 判斷其它頂點若與當前頂點存在邊且未訪問過 */ if(G.arc[i][j] == 1 && !visited[j]) { visited[j] = TRUE; /* 將找到的此頂點標記為已訪問*/ printf("%c ", G.vexs[j]); /* 打印頂點 */ EnQueue(&Q, j); /* 將找到的此頂點入隊列 */ } } }//while } }//for } int main(void) { Graph graph; CreateGraph(&graph); printf("廣度遍歷:"); BFSTraverse(graph); return 0; } 

運行結果:

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM