一、深度優先遍歷的定義
深度優先遍歷(Depth_First_Search),也稱爲深度優先搜索,簡稱DFS;
深度優先其實是一個遞歸過程,類似於樹的前序遍歷;它從圖的某個頂點出發,訪問此頂點,然後從該頂點的未被訪問的鄰接頂點出發深度優先遍歷圖,直至圖中所有和該頂點有路徑相通的頂點都被訪問到了;若此時圖中尚有頂點未被訪問,則另選圖中一個未曾被訪問的頂點做起始點,重複上述過程,直至圖中所有頂點都被訪問到爲止;
二、深度優先代碼實現
bool visited[20];訪問標誌數組,與頂點表對應,當標誌爲true時,說明該頂點已經被訪問過;
if(graph.arc[i][j] && !visited[j]);這裏是在判斷頂點i是否與j鄰接,如果鄰接,那麼判斷j是否已經被訪問過,如果未訪問過,那麼遞歸調用DFS(graph,j);
bool visited[20];
void DFS(Graph& graph, int i){
visited[i] = true;
cout << graph.vexs[i] << " ";
for(int j = 0; j < graph.vexNum; ++j) {
if(graph.arc[i][j] && !visited[j]) {
DFS(graph, j);
}
}
}
void DFSTraverse(Graph &graph) {
//初始化訪問標誌數組
for(int i = 0; i < graph.vexNum; ++i) {
visited[i] = false;
}
//這裏遍歷是爲了防止該圖不是連通圖,一次遍歷不完的情況發生
for(int i = 0; i < graph.vexNum; ++i) {
if(!visited[i]) {
DFS(graph, i);
}
}
}
DFSTraverse和鄰接矩陣市面沒有區別的,DFS遍歷方式有些差異,鄰接矩陣是遍歷該頂點的在矩陣中的一個橫行,鄰接表遍歷的是該頂點的鄰接鏈表;if(!visited[p->vexOrderNum]),這裏就能很明顯比較兩種存儲結構的差異,鄰接表上的節點一定是鄰接頂點,而鄰接矩陣是需要判斷的,感覺自己好像在說廢話一樣;
bool visited[MAXVEX];
void DFS(Graph& graph, int i){
visited[i] = true;
cout << graph.vexArr[i].vexName << " ";
eNode* p = graph.vexArr[i].vNext;
while(p) {
if(!visited[p->vexOrderNum]) {
DFS(graph, p->vexOrderNum);
}
p = p->next;
}
}
void DFSTraverse(Graph &graph) {
for(int i = 0; i < graph.vexNum; ++i) {
visited[i] = false;
}
for(int i = 0; i < graph.vexNum; ++i) {
if(!visited[i]) {
DFS(graph, i);
}
}
}
三、測試
#include<iostream>
using namespace std;
typedef struct Graph {
char vexs[20];
int arc[20][20];
int vexNum, edgeNum;
}*pGraph;
void CreateGraph(Graph &graph) {
cout << "輸入頂點數和邊數:";
cin >> graph.vexNum >> graph.edgeNum;
for(int i = 0; i < graph.vexNum; ++i) {
cout << "輸入第" << i+1 << "個頂點的名稱:";
cin >> graph.vexs[i];
}
for(int i = 0; i < graph.vexNum; ++i) {
for(int j = 0; j < graph.vexNum; ++j) {
graph.arc[i][j] = 0;
}
}
int x, y, w;
for(int i = 0; i < graph.edgeNum; ++i) {
cout << "請輸入第" << i+1 << "條邊的兩個頂點x,y和權值w:";
cin >> x >> y >>w;
graph.arc[x][y] = w;
graph.arc[y][x] = w;
}
}
bool visited[20];
void DFS(Graph& graph, int i){
visited[i] = true;
cout << graph.vexs[i] << " ";
for(int j = 0; j < graph.vexNum; ++j) {
if(graph.arc[i][j] && !visited[j]) {
DFS(graph, j);
}
}
}
void DFSTraverse(Graph &graph) {
for(int i = 0; i < graph.vexNum; ++i) {
visited[i] = false;
}
for(int i = 0; i < graph.vexNum; ++i) {
if(!visited[i]) {
DFS(graph, i);
}
}
}
int main() {
Graph graph;
CreateGraph(graph);
for(int i = 0; i < graph.vexNum; ++i) {
cout << "\t" << graph.vexs[i];
}
cout << "\n\n";
for(int i = 0; i < graph.vexNum; ++i) {
cout << graph.vexs[i] << "\t";
for(int j = 0; j < graph.vexNum; ++j) {
cout << graph.arc[i][j] << "\t";
}
cout << "\n\n";
}
cout << "深度優先遍歷結果:";
DFSTraverse(graph);
getchar();
return 0;
}
這個遍歷結果剛好是按順序的從A到I,這不是個巧合,其實在上面的鄰接矩陣中就可以看出來爲什麼剛好是這樣的遍歷順序~
其實鄰接表遍歷的順序在上面打印的表結構也能可看出來~
四、總結
其實深度遍歷的思路就是,利用圖的頂點之間的鄰接關係來遍歷,爲了防止重複遍歷,給訪問過的頂點打上tag;如果你對鄰接表和鄰接矩陣理解的比較透徹的話,深度優先遍歷是很好理解的;
針對鄰接矩陣和鄰接表兩種存儲方式的深度優先遍歷來說,n個頂點,e個邊的圖,時間複雜度分別爲O(n2)和O(n + e);所以如何做選擇存儲結構還是需要根據實際情況來看;