算法思想:
訪問給定的起始點v,從v出發,訪問它的一個不曾被訪問過的鄰接頂點w1;再從w1出發,訪問w1的一個不曾被訪問過的鄰接頂點w2;…,如此下去,直至到達了一個頂點,它沒有未訪問的鄰接頂點爲止。退回一步到上一個被訪問的頂點,看它是否還有未被訪問的鄰接頂點。若有,則訪問該鄰接頂點,且從它出發,進行前述的類似訪問。若沒有,則再退回一步進行搜索。當所有頂點均被訪問,則過程終止。
注意我們下面給出的實現,都是基於前面講解的圖的鄰接表的實現。由於前面給出的鄰接表實現是基於有向權圖,而我們現在要講解的圖的遍歷幾乎與權值無關,因此我們可以修改前面的代碼作爲非權圖使用。但是此處我們仍然採用前面的實現,邊的權值可以設爲0、1、或其他任意值(這裏我們給出了權值,但是我們不用它)。
一、遞歸實現
1、作爲非成員函數
注意前面給出的Graph爲一個模板類,所以該函數是模版函數
#include <cstdlib>
#include <iostream>
#include "graph.h"
/*從序號爲v的頂點出發,深度優先遍歷圖
*遞歸實現1
*/
template<class T>
void DFS(const Graph<T> &g, const int v, vector<bool>& visit)
{
cout<<g.getVertexName(v)<<" ";//輸出序號爲v的頂點的名稱
visit[v] = true; //標記頂點v已被訪問
int w = g.getFirstNeighbor(v);//v的第一個鄰接頂點
while(w != -1) //若存在鄰接頂點
{
if(!visit[w]) //鄰接頂點w未被訪問過
{
DFS(g, w, visit);
}
w = g.getNextNeighbor(v,w);//獲得v相對w的下一個鄰接頂點
}
}
int main(int argc, char *argv[])
{
Graph<char> graph("graph2.dat");
cout<<"after read the graph :"<<endl;
graph.printGraph();
int n = graph.getNumberOfVertex();
vector<bool> visit(n,false);
cout<<"DFS: ";
DFS(graph,0,visit);
cout<<endl<<endl;
system("PAUSE");
return EXIT_SUCCESS;
}
測試結果:
(1)使用測試文件graph2.dat輸出爲:
DFS: A G F B C E D
(2)使用測試文件graph3.dat輸出爲:
DFS: A C B
我們發現,對於連通圖,該算法是正確的,但是對於非連通圖,上面的算法是錯誤的,它不能完整地遍歷圖的各個頂點。正確的算法如下所示:
#include <cstdlib>
#include <iostream>
#include "graph.h"
/*從序號爲v的頂點出發,深度優先遍歷圖
*遞歸實現1
*/
template<class T>
void DFS(const Graph<T> &g, const int v, vector<bool>& visit)
{
cout<<g.getVertexName(v)<<" ";//輸出序號爲v的頂點的名稱
visit[v] = true; //標記頂點v已被訪問
int w = g.getFirstNeighbor(v);//v的第一個鄰接頂點
while(w != -1) //若存在鄰接頂點
{
if(!visit[w]) //鄰接頂點w未被訪問過
{
DFS(g, w, visit);
}
w = g.getNextNeighbor(v,w);//獲得v相對w的下一個鄰接頂點
}
}
template<class T>
void depthFirstSearch(const Graph<T> &g, vector<bool>& visit)
{
int n = g.getNumberOfVertex();
for(int v=0; v<n; v++)
{
if(!visit[v])
{
DFS(g,v,visit);
}
}
}
int main(int argc, char *argv[])
{
Graph<char> graph("graph3.dat");
cout<<"after read the graph :"<<endl;
graph.printGraph();
int n = graph.getNumberOfVertex();
vector<bool> visit(n,false);
cout<<"DFS: ";
// DFS(graph,0,visit);
depthFirstSearch(graph,visit);
cout<<endl<<endl;
system("PAUSE");
return EXIT_SUCCESS;
}
2、作爲成員函數
有了上面的講解,這個就比較簡單了
在private部分添加如下函數聲明:
void depthFirstSearch(const int v, bool* visit)const;
在public部分添加如下函數聲明:
void depthFirstSearch()const;
實現如下://private深度優先遍歷遞歸函數的實現
template<class T>
void Graph<T>:: depthFirstSearch(const int v, bool* visit)const
{
cout<<getVertexName(v)<<" ";//輸出序號爲v的頂點的名稱
visit[v] = true; //標記頂點v已被訪問
int w = getFirstNeighbor(v);//v的第一個鄰接頂點
while(w != -1) //若存在鄰接頂點
{
if(!visit[w]) //鄰接頂點w未被訪問過
{
depthFirstSearch(w, visit);
}
w = getNextNeighbor(v,w);//獲得v相對w的下一個鄰接頂點
}
}
//public深度優先遍歷實現
template<class T>
void Graph<T>:: depthFirstSearch()const
{
int n = getNumberOfVertex();
bool * visit = new bool[n];
for(int i=0; i<n; i++)
{
visit[i] = false;
}
for(int v=0; v<n; v++)
{
if(!visit[v])
{
depthFirstSearch(v,visit);
}
}
delete[] visit;
}
二、迭代實現
基本思想:
(1)檢測堆棧是否爲空。若堆棧爲空,則迭代結束;否則,從棧中彈出一個頂點v;
(2) 訪問v,將visit[v]值更新爲true;
(3) 求出v的鄰接頂點表,將v的未被訪問的鄰接頂點壓入棧,執行步驟(1)。
由於不想每次更改Graph.h頭文件,所以下面就直接給出非成員函數的實現,有興趣自己可以參照上面給出成員函數實現。
#include <cstdlib>
#include <iostream>
#include <stack>
#include "graph.h"
template<class T>
void iterativeDFS(const Graph<T>& g, const int v, vector<bool>& visit)
{
stack<int> s;
s.push(v);
int w,k;
while(!s.empty())
{
w = s.top();
s.pop();
if(!visit[w])//頂點w未被訪問
{
visit[w] = true;
cout<<g.getVertexName(w)<<" ";
k = g.getFirstNeighbor(w);
while(k != -1)
{
if(!visit[k])
{
s.push(k);// 若k未被訪問過,將k壓入棧
}
k = g.getNextNeighbor(w,k);
}
}
}
}
template<class T>
void iDepthFirstSearch(const Graph<T>& g, vector<bool>& visit)
{
int n = g.getNumberOfVertex();
for(int v=0; v<n; v++)
{
if(!visit[v])
iterativeDFS(g,v,visit);
}
}
int main(int argc, char *argv[])
{
Graph<char> graph("graph3.dat");
cout<<"after read the graph :"<<endl;
graph.printGraph();
int n = graph.getNumberOfVertex();
vector<bool> visit(n,false);
cout<<"IDFS: ";
iDepthFirstSearch(graph,visit);
cout<<endl<<endl;
system("PAUSE");
return EXIT_SUCCESS;
}
三、測試文件
1、測試文件graph2.dat
7
AB C D E F G
A B 1
A G 1
B C 1
C D 1
C E 1
D E 1
E F 1
F A 1
G F 1
2、測試文件graph3.dat
5
A B C D E
A B 1
A C 1
D E 1
注意:
深度優先遍歷序列不是唯一的,它與鄰接表的實際存儲內容有關,也與採用的算法是遞歸還是迭代有關。
參考資料:
[1]《數據結構》劉大有 唐海鷹等著 高等教育出版社
[2][嚴蔚敏《數據結構(C語言版)》