一、介紹
- 廣度優先遍歷
廣度優先遍歷從某個頂點 v 出發,首先訪問這個結點,並將其標記為已訪問過,然後順序訪問結點v的所有未被訪問的鄰接點 {vi,..,vj} ,並將其標記為已訪問過,然後將 {vi,…,vj} 中的每一個節點重複節點v的訪問方法,直到所有結點都被訪問完為止。分為三個步驟:
- 使用一個輔助佇列,首先將第一個節點v放入佇列,並標記也被訪問,然後檢測佇列是否為空
- 如果佇列不為空時,將佇列的第一個元素取出,並將與該節點相連且未被訪問的節點加入佇列,並將這些節點進行標記
- 當佇列為空時,就完成了圖的廣度優先遍歷
- 無權圖的最短路徑
無權圖的最短路勁,是基於圖的廣度優先遍歷而來的,是指圖中兩節點間最短的路徑,透過ord[i]陣列用來記錄上一個節點到i節點的路徑
二、遍歷過程
下面直接來看看具體的例子:
對下圖進行廣度優先遍歷
從節點0開始
將0放入佇列中,並對節點0進行標記
當佇列不為空時,就從佇列中拿出首元素,即節點0;然後將和節點0兩連並未被訪問過的節點依次放入佇列中,並進行標記,如下圖:
同理,此時佇列不為空,就拿出佇列中的首元素節點1,此時沒有和節點1相連且未被訪問過的節點,就沒有節點進入佇列,然後進入下一輪的遍歷。
佇列不為空,取出佇列首元素節點2,同理(參考節點1的出佇列),進入下一輪遍歷
佇列不為空,取出佇列首元素節點5,並將和5相連且未被訪問的節點放入佇列,並標記,如下圖:
佇列不為空,拿出佇列首元素節點6,此時節點6相連的且未被訪問過的節點就為空了,進入下一輪遍歷
同理,遍歷節點3:
同理,遍歷節點4;此時佇列為空,遍歷已就完成了
三、程式碼實現
編寫一個類,成員變數及其初始化:
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <cassert>
using namespace std;
//尋找無權圖的最短路徑
template <typename Graph> //封裝為統一介面
class ShortestPath{
private:
Graph &G; //圖是引用
int s; //起始點
bool *visited; //記錄dfs的過程中節點是否被標記
int *from; //記錄路徑,from[i]表示查詢的路徑上i的上一個節點
int *ord; //記錄路徑中節點的次序,ord[i]表示i節點在路徑中的次序
public:
//建構函式
ShortestPath(Graph &graph, int s):G(graph){
//演算法初始化
assert( s >= 0 && s < graph.V() );
this->s = s;
visited = new bool[graph.v()];
from = new int[graph.v()];
ord = new int[graph.v()];
for(int i = 0; i < graph.v(); i++){
visited[i] = false;
from[i] = -1;
ord[i] = -1;
}
廣度優先遍歷演算法:
// 無向圖最短路徑演算法, 從s開始廣度優先遍歷整張圖
queue<int> q; //q為輔助佇列
q.push( s );
visited[s] = true;
ord[s] = 0;
while( !q.empty() ){
//將佇列中的首元素賦值給v
int v = q.front();
q.pop(); //將第一個元素取出佇列
typename Graph::adjIterator adj(G, v);
for( int i = adj.begin(); !adj.end(); i = adj.next() ){
if( !visited[i] ){ //判斷節點是否被訪問過
q.push(i);
visited[i] = true;
from[i] = v;
ord[i] = ord[v] + 1; //記錄最短路徑
}
}
}
}
解構函式及其成員函式
//解構函式
~ShortestPath(){
delete[] visited;
delete[] from;
delete[] ord;
}
// 查詢從s點到w點是否有路徑
bool hasPath(int w) {
assert(w >= 0 && w < G.V());
return visited[w];
}
// 查詢從s點到w點的路徑, 存放在vec中
void path(int w,vector<int> vec){
assert(w >= 0 && w < G.V());
stack<int> s;
// 透過from陣列逆向查詢到從s到w的路徑, 存放到棧中
int p = w;
while(p != -1){
s.push(p);
p = from[p];
}
// 從棧中依次取出元素, 獲得順序的從s到w的路徑
vec.clear();
while( !s.empty() ){
vec.push_back( s.top());
s.pop();
}
}
// 列印出從s點到w點的路徑
void showPath(int w){
assert( w >= 0 && w < G.V() );
vector<int> vec;
path(w, vec);
for( int i = 0 ; i < vec.size() ; i ++ ){
cout<<vec[i];
if( i == vec.size()-1 )
cout<<endl;
else cout<<" -> ";
}
}
// 檢視從s點到w點的最短路徑長度
int length(int w){
assert( w >= 0 && w < G.V() );
return ord[w];
}
};
本作品採用《CC 協議》,轉載必須註明作者和本文連結