(邊自學邊寫,還真有點累啊,)
注:以下代碼均為部分,關於圖的表示方法參看我的博客:
http://www.cnblogs.com/dzkang2011/p/graph_1.html
一、廣度優先搜索
廣度優先搜索(BFS)是最簡單的圖搜索算法之一,也是很多重要的圖算法的原型。在Prim最小生成樹算法和Dijkstra單源最短路徑算法中,都采用了與廣度優先搜索類似的思想。
在給定圖G=(V,E)和一個特定的源頂點s的情況下,廣度優先搜索系統地探索G中的邊,以期發現可以從s到達的所有頂點,並計算s到所有這些可達頂點之間的距離(即最少的邊數)。該搜索算法同時還能生成一棵根為s、且包括所有s的可達頂點的廣度優先樹。對從s可達的任意頂點v,廣度優先樹中從s到v的路徑對應於圖G中從s到v的一條最短路徑,即包含最少邊數的路徑。該算法對有向圖和無向圖同樣適用。之所以稱為廣度優先搜索,是因為它始終是將已發現和未發現頂點之間的邊界,沿其廣度方向向外擴展。亦即,算法首先發現和s距離為k的所有頂點,然后才會發現和s距離為k+1的其他頂點。
在廣度優先搜索中,我們首先要注意一個頂點的3種狀態:
已到達(已訪問)但還未考察:表示當前搜索到的這個頂點,接下來要對這個頂點進行分析。
正在考察:已經到達了這個頂點,但是它的相鄰頂點還沒有完全被訪問到,稱為未考察完成。采用隊列存儲未被考察的頂點。
已考察:已經到達了這個頂點,且它的所有相鄰頂點都已經被訪問完成,則稱該頂點已考察。
具體步驟如下:
(1)從頂點s開始,標記它為已訪問,將其壓入隊列中;
(2)這時,頂點s的的相鄰頂點還未被訪問,所以s暫時還為未考察狀態;
(3)訪問它的所有相鄰頂點,並將它們壓入隊列,之后s的狀態變為已考察,將其從隊列中刪除;這些入隊的頂點的父親頂點即為s
(4)取隊頭頂點,以其為起點重復進行以上操作。當所有的頂點都被考察完畢,即隊列為空時,考察停止。
由分析可知,廣度優先搜索的總運行時間為O(V+E),為一個線性時間。
最短路徑:源頂點為s,d[v]中保存從s到v的最短路徑長度
廣度優先樹:過程BFS在搜索圖的同時,也建立了一棵廣度優先樹,這棵樹是用parent[]表示的,parent[v]表示的是頂點v的父親結點。

廣度優先搜索C++(鄰接表)實現:
1 #define INF 9999 2 bool visited[maxn]; //標記頂點是否被考察,初始置為false 3 int d[maxn], parent[maxn]; //d[]記錄最短路徑長度,parent[]記錄某結點的父親結點,生成樹 4 void bfs(int s) //廣度優先搜索,鄰接表輸入(詳見“一、圖的表示”) 5 { 6 for(int i = 1; i <= n; i++) //初始化 7 { 8 d[i] = INF; 9 parent[i] = -1; 10 visited[i] = false; 11 } 12 visited[s] = true; 13 d[s] = 0; 14 queue<int> q; //用STL隊列實現 ,#include <queue> 15 q.push(s); //壓入隊列 16 while(!q.empty()) 17 { 18 int u = q.front(); //取隊頭元素 19 arcnode * p = Ver[u].firarc; 20 while(p != NULL) //遍歷相鄰頂點 21 { 22 if(!visited[p->vertex]) 23 { 24 q.push(p->vertex); //壓入隊列 25 parent[p->vertex] = u; //指向父親 26 d[p->vertex] = d[u]+1; //路徑長+1 27 visited[p->vertex] = true; //置為已被考察 28 } 29 p = p->next; 30 } 31 q.pop(); //出隊列 32 } 33 } 34 void PrintPath(int s, int v) //打印從s到v的最短路徑,需先調用bfs(s) 35 { 36 if(v == s) 37 cout << s << endl; 38 else if(parent[v] == -1) 39 return; 40 else 41 { 42 PrintPath(s,parent[v]); 43 cout << v << endl; 44 } 45 }
二、深度優先搜索
深度優先搜索算法所遵循的搜索策略是盡可能“深”地搜索一個圖。在深度優先搜索中,對於最新發現的頂點,如果它還有以此為起點而未探測到的邊,就沿此邊繼續探測下去。當頂點v的所有邊都已被探尋過后,搜索將回溯到發現頂點v有起始點的那些邊。這一過程一直進行到已發現從源頂點可達的所有頂點時為止。如果還存在未被發現的頂點,則選擇其中一個作為源頂點,並重復以上過程。整個過程反復進行,直到所有的頂點都已被發現為止。
與廣度優先搜索類似,在深度優先搜索中,每當掃描已發現頂點u的鄰接表,從而發現新頂點v時,就將置v的父域parent[v]為u。與廣度優先搜索不同的是,其先輩子圖形成一棵樹,深度優先搜索產生的先輩子圖可以由幾棵樹所組成,因為搜索可能由多個源頂點開始重復進行。深度優先搜索的先輩子圖形成了一個由數棵深度優先樹所組成的深度優先森林。
除了創建一個深度優先森林外,深度優先搜索同時為每個頂點加蓋時間戳。每個頂點v有兩個時間戳:當頂點v第一次被發現時,記錄下第一個時間戳d[v],當結束檢查v的鄰接表時,記錄下第二個時間戳f[v]。許多圖的算法都用到了時間戳,它們對推算深度優先搜索的進行情況有很大幫助。
具體步驟如下:
(1)初始化:將所有頂點置為未訪問,其父親結點均為-1
(2)遍歷每一個頂點u,如果它未訪問,則對該頂點進行(3)操作
(3)置該頂點u為正在訪問。對於u的每一個相鄰頂點v,如果v未被訪問,對v重復(3)操作,直至找到所有相鄰頂點為止。置頂點u為已訪問。
(4)若所有的頂點都被考察,則搜索停止。

深度優先搜索C++(鄰接表)實現:
1 #define INF 9999 2 bool visited[maxn]; //標記頂點是否被考察,初始值為false 3 int parent[maxn]; //parent[]記錄某結點的父親結點,生成樹,初始化為-1 4 int d[maxn], time, f[maxn]; //時間time初始化為0,d[]記錄第一次被發現時,f[]記錄結束檢查時 5 void dfs(int s) //深度優先搜索(鄰接表實現),記錄時間戳,尋找最短路徑 6 { 7 cout << s << " "; 8 visited[s] = true; 9 time++; 10 d[s] = time; 11 arcnode * p = Ver[s].firarc; 12 while(p != NULL) 13 { 14 if(!visited[p->vertex]) 15 { 16 parent[p->vertex] = s; 17 dfs(p->vertex); 18 } 19 p = p->next; 20 } 21 time++; 22 f[s] = time; 23 } 24 void dfs_travel() //遍歷所有頂點,找出所有深度優先生成樹,組成森林 25 { 26 for(int i = 1; i <= n; i++) //初始化 27 { 28 parent[i] = -1; 29 visited[i] = false; 30 } 31 time = 0; 32 for(int i = 1; i <= n; i++) //遍歷 33 if(!visited[i]) 34 dfs(i); 35 cout << endl; 36 }
(持續更新......)
