深度優先搜索算法和廣度優先搜索算法是圖論中兩個有意思也很實用的算法,下面我們來看看這兩個算法。
嚴書中,給出的利用深度優先搜索(Deep First Search)算法進行圖的遍歷偽碼如下
1 Boolean visited[MAX]; //標志數組 2 Status (*VisitFunction)(int v); //訪問函數 3 4 void DFSTraverse(Graph G, Status (*Visit)(int v)) 5 { 6 VisitFunction = Visit; 7 for(v = 0; v < G.vexnum; ++v) //初始化標志數組 8 visited[v] = false; 9 for(v = 0; v < G.vexnum; ++v) //對未訪問過的頂點調用DFS 10 if(!visited[v]) 11 DFS(G, v); 12 } 13 14 void DFS(Graph G, int v) //從頂點v出發進行DFS 15 { 16 visited[v] = true; 17 VisitFunction(v); 18 for(w = FirstAdjVex(G, v); w >= 0; w = NextAdjVex(G, v, w)) 19 if(!visited[w]) 20 DFS(G, w); //遞歸調用未訪問的鄰接頂點w 21 }
進行深度優先搜索和廣度優先搜索需要記錄頂點訪問情況,因為圖中的環會對遍歷圖造成麻煩。要解決此問題可以使用一個初始值為false的visited數組,其下標為頂點在相應的存儲結構中的對應下標,當一個頂點被訪問后,visited數組中其對應的值變為true。在上面給出的算法中第9行代碼是為了解決在非連通圖中會出現的對一個頂點進行深度優先搜索不完全的情況。14行開始的深度優先搜索算法基於遞歸,對於傳入的圖G以及一個頂點v,當w = FirstAdjVex(G, v)得到的頂點w的visited情況為false時會一直訪問FirstAdjVex,而當遇到visited[w] = false的情況則表明從頂點v開始每次從FirstAdjVex獲得頂點形成的路徑已經搜索完畢,隨着彈出棧,上一級的NextAdjVex開始搜尋下一個鄰接頂點,重復這個過程,最后v所在連通圖就能夠被深度優先遍歷。
對應與上面的圖,由於其是連通圖,只要關注DFS算法部分就可以。當傳入參數是G和v1時,首先標記v1的visited情況為true(以后簡稱v1為true,false同理),訪問v1,v1的FirstAdjVex為v2,因此w = v2,由於v2為false,調用DFS本身,標記v2為true,訪問v2,接下來v4, v8, v5同理,而v5的FirstAdjVex為v2,v2已被訪問過visited值為true,w試圖獲取v5的NextAdjVex,但v5並無NextAdjVex,本次調用結束,返回上一層,同理一直返回到v2,v2有NextAdjVex v5,但其已被訪問過,而v2無下一NextAdjVex,繼續返回到v1,v1的NextAdjVex為v3,可以訪問,v3的FirstAdjVex v6可以訪問,同理一直訪問到v7,此時v7和此前v5狀況一樣,於是一直返回,v1返回后整個DFS函數結束。
DFS順序v1 v2 v4 v8 v5 v3 v6 v7。
嚴書中的廣度優先遍歷算法偽碼如下:
1 void BFSTraverse(Graph G, Status (*Visit)(int v)) 2 { 3 for(v = 0; v < G.vexnum; ++v) 4 visited[v] = false; 5 InitQueue(Q); 6 for(v = 0; v < G.vexnum; ++v) 7 if(!visited[v]) 8 { 9 visited[v] = true; 10 Visit(v); 11 EnQueue(Q, v); 12 while(!QueueEmpty(Q)) 13 { 14 DeQueue(Q, u); 15 for(w = FirstAdjVex(G, u); w >= 0; w = NextAdjVex(G, u, w)) 16 if(!visited[w]) 17 { 18 visited[w] = true; 19 visit(w); 20 EnQueue(Q, w); 21 } 22 } 23 } 24 }
為了實現廣度優先搜索,利用了隊列這一數據結構,第6行的for循環是為了處理非聯通圖,我們以頂部的圖為例,該循環只會經歷一次。以v1開始這個循環,首先標記v1為true,訪問v1,再入隊列,此時隊列元素為<v1>,Q不為空,出隊列,並將隊列元素賦值給u,這部后隊列為<>,接下來的for循環以u的FirstAdjVex即v2開始,標記v2,訪問v2,將v2入隊列,此時隊列為<v2>,回到15行的for循環,u的NextAdjVex即v3被賦值給w,同樣的,標記v3,訪問v3,v3入隊列,此時隊列<v2, v3>, 之后再回到15行,而v1此時無NextAdjVex,返回12行,隊列不空,v2出隊列賦給u,此時隊列<v3>,與之前同樣的,標記並訪問入隊列v4,標記並訪問入隊列v5,返回12行,此時隊列<v3, v4, v5>,v3出隊列賦給u,依次標記並訪問入隊列v6,v7,再回到12行,隊列為<v4, v5, v6, v7>,v4出隊列,標記並訪問入隊列v8,返回12行,此時隊列<v5, v6, v7, v8>,v5出隊列賦值給u,由於v5的AdjVex v8已被訪問,回到12行,同理v6, v7, v8出隊列,最后隊列為空,函數結束。
BFS順序v1 v2 v3 v4 v5 v6 v7 v8
