上一次已經提到,圖的遍歷通常有兩種算法,即廣度優先和深度優先。其中廣度優先搜索算法會從指定的第一個頂點開始遍歷圖,先訪問其全部的相鄰點,就像一次訪問圖的一層。換句話說,就是先寬後深地訪問頂點,如圖1。javascript
圖1java
從頂點v開始的廣度優先搜索的步驟以下:算法
若是Q非空,則運行如下步驟:segmentfault
咱們用三種狀態來反映頂點的狀態:數組
所以在算法開始執行時,須要將全部頂點置爲白色;以下代碼所示(本文的全部代碼都是在圖中實現的Graph類中添加的),其中vertices保存着圖全部頂點的名字。數據結構
function Graph() { var vertices = []; var adjList = new Dictionary(); // 圖類的其餘代碼省略... var initializeColor = function(){ var color = []; for (var i=0; i<vertices.length; i++){ color[vertices[i]] = 'white'; } return color; }; }
廣度優先搜索算法的核心代碼以下,其中隊列Queue的實現參考基於JavaScript的數據結構 — 隊列的實現函數
this.bfs = function(v, callback){ var color = initializeColor(), // 將全部頂點初始化爲白色 queue = new Queue(); // 實例化隊列 queue.enqueue(v); // 將起始頂點v加入隊列 while (!queue.isEmpty()){ // 一直循環處理隊列,直到隊列爲空 var u = queue.dequeue(), // 移除隊列頂部的元素,並取得該頂點 neighbors = adjList.get(u); // 獲取該頂點的相鄰頂點 color[u] = 'grey'; // 將該頂點置爲灰色,代表該頂點被訪問過,但並未被探索過 for (var i=0; i<neighbors.length; i++){ // 循環處理該頂點的相鄰頂點 var w = neighbors[i]; if (color[w] === 'white'){ // 若是該頂點沒有被訪問過 color[w] = 'grey'; // 將該頂點置爲灰 queue.enqueue(w); // 將該頂點加入隊列 } } color[u] = 'black'; // 該頂點訪問已經被徹底訪問,將其置爲黑 callback && callback(u); // 用回調函數處理該節點(若是有) } };
因爲廣度優先算法是一層層往下遍歷的,即先訪問與起始頂點距離爲1的點,再訪問距離爲2的點,以此類推。所以,給定一個圖G和源頂點v, 要找出每個頂點u與v之間的最短距離(以邊的數量計算),能夠在上述代碼的基礎上作必定修改(修改的位置用空行隔開):this
// 獲取路徑信息 this.pathData = function(v){ var color = initializeColor(), queue = new Queue(), d = new Array(vertices.length).fill(0), // 用於保存起始頂點v到任意頂點u的距離 pred = new Array(vertices.length).fill(null); // 用於保存v到u的路徑上u的上一級頂點(前溯點) queue.enqueue(v); while (!queue.isEmpty()){ var u = queue.dequeue(), neighbors = adjList.get(u); color[u] = 'grey'; for (i=0; i<neighbors.length; i++){ var w = neighbors[i]; if (color[w] === 'white'){ color[w] = 'grey'; d[w] = d[u] + 1; // w到u的距離差是1 pred[w] = u; // w的上一級頂點是u queue.enqueue(w); } } color[u] = 'black'; } return { // 返回保存的數據 distances: d, predecessors: pred }; }; // 格式化輸出路徑信息 this.printPathData = function (pathData) { var fromVertex = vertices[0]; // 獲取起始點 for (var i=1; i<vertices.length; i++){ var toVertex = vertices[i], // 要到達的頂點 path = []; // 用於保存路徑 // 從目標頂點一直回溯到起始頂點 for (var v=toVertex; v!== fromVertex; v= pathData.predecessors[v]) { path.push(v); // 頂點添加進路徑 } path.push(fromVertex); // 將起始頂點添加進路徑 var s = path.pop(); while (path.length > 0){ s += ' - ' + path.pop(); // 從路徑數組倒序輸出頂點 } console.log(s); } }
經過執行Graph.printPathData(Graph.pathData())便可輸出起始頂點到每個頂點的最短路徑。spa
對於加權圖的最短路徑,廣度優先算法可能並不合適。好比,Dijkstra’s算法能夠解決單源最短路徑問題。Bellman–Ford算法解決了邊權值爲負的單源最短路徑問題。A*搜索算法解決了求僅一對頂點間的最短路徑問題,它用經驗法則來加速搜索過程。Floyd–Warshall算法解決了求全部頂點對間的最短路徑這一問題。code