更正一下前面一篇博文中的不妥之處:前面一篇博文有非無向圖而不可廣度優先遍歷之嫌。其實廣度優先搜索也是可以用在有向圖中的,因為無向圖是特殊的有向圖,而這里廣度優先遍歷為的是遍歷連通圖的所有節點,對於有向圖來說只要存在一條有向邊,那么兩個節點就是“連接”的狀態,那么遍歷就可以纏繞在這條邊上。
深度優先遍歷,也就是盡量深的遍歷圖的節點,同樣用到了三色標記,用到了遞歸函數,這里還有一個蓋時間戳的操作。當發現某個節點時記錄一下時間,當完成一個節點的遍歷時記錄一下時間,這樣依據時間戳,可以發現很多性質和應用。
其實在很多地方廣度優先搜索和深度優先搜索代碼實現起來都有特點:廣度優先搜索使用一個queue來組織循環,而深度優先搜索則是使用遞歸函數來實現“深度”優先。
深度優先遍歷的第一個應用是拓撲排序:
/** * 這里使用鄰接矩陣表示一個無向連通圖。 */ #include <iostream> #include <list> #include <algorithm> using namespace std; #define LEN 10 #define NIL -1 bool m[LEN][LEN]; enum COLOR {WHITE, GRAY, BLACK}; COLOR color[LEN]; int d[LEN]; int p[LEN]; int t; int f[LEN]; list<int> topolist; void DFS_VISIT(int u){ color[u] = GRAY; t++; d[u] = t; int i; for(i=0;i<LEN;++i){ if(m[u][i] == true){ if(color[i] == WHITE){ p[i] = u; DFS_VISIT(i); } } } color[u] = BLACK; f[u] = t = t +1; topolist.push_front(u); //cout<<u<<endl; } void DFS(){ int i; for(i=0;i<LEN;++i){ color[i] = WHITE; p[i] = NIL; } t = 0; for(i=0;i<LEN;++i) if(color[i] == WHITE) DFS_VISIT(i); } void printPath(int s, int v){ if(v == s) cout<<s<<endl; else if(p[v] == NIL){ cout<<"No path exists"<<endl; return ; } else{ printPath(s, p[v]); cout<<v<<endl; } } void topological_sort(){ DFS(); } void print(int s){ cout<<s<<endl; } int main(){ int i,j; for(i=0;i<LEN;++i){ for(j=0;j<LEN;++j){ m[i][j] = false; } } m[0][2] = true; m[2][0] = true; m[1][7] = true; m[7][1] = true; m[2][7] = true; m[7][2] = true; m[2][4] = true; m[4][2] = true; m[7][3] = true; m[3][7] = true; m[3][4] = true; m[4][3] = true; m[4][5] = true; m[5][4] = true; m[5][8] = true; m[8][5] = true; m[8][6] = true; m[6][8] = true; m[8][9] = true; m[9][8] = true; //DFS(); //printPath(0, 9); topological_sort(); for_each(topolist.begin(), topolist.end(), print); return 0; }
利用DFS進行拓撲排序,只需要在遍歷節點完成記錄時間戳時將該節點插入到一個用於記錄拓撲序列的list前端就ok了。
深度優先搜索的第二個應用是:對強連通分支的划分
算法的過程很簡單,分為以下兩部:
首先調用DFS獲得圖G的節點的完成時間記錄,
再對圖G的轉置進行深度優先遍歷,這個時候根據第一步得到的完成時間記錄降序進行遍歷。
這樣完成之后就得到了圖G的幾個強連通分支。