資料結構——圖(6)——深入分析BFS演算法
阿新 • • 發佈:2018-11-10
DFS的不足和BFS演算法
雖然我們知道根據DFS演算法我們可以找到所有的,由起始節點到目標節點的所有路徑,但並不代表那條路是最短的或者是最佳的。就像我們上篇文章所說的一樣,對於同一幅圖,非遞迴演算法找到的路徑就明顯比遞迴演算法找的要短。
回顧我們之前提到的BFS的基本思想:從起始頂點開始,首先探索鄰居節點,然後再移動到下一級鄰居。
假設我們從a走到i,按照BFS演算法,應該是
a -> d-> h ->i (並且這是一條最短的路徑)
其BFS虛擬碼如下:
bfs from v1 to v2: //建立一個佇列q來儲存走過的路徑path(可以用vector來儲存路徑) create a queue of paths (a vector), q //將節點v1的路徑入隊 q.enqueue(v1 path) //當q不為空,並且節點V2未被訪問時 while q is not empty and v2 is not yet visited: //將q中的元素移出佇列,並且存入path中 path = q.dequeue() //將路徑中的最後元素賦值給節點型變數v v = last element in path //如果v未被訪問 if v is not visited: //標記節點V mark v as visited //如果節點是目標節點,停止執行 if v is the end vertex, we can stop. //遍歷V中所有未被標記的鄰居 for each unvisited neighbor of v: //將v節點的鄰居作為最後的元素構成新的路徑 make new path with v's neighbor as last element //將新的路徑排入佇列中 enqueue new path onto q
沒錯 這是一個很複雜的步驟。理解都有點困難更別提記住了,但是我們可以注意到,這個實現過程跟DFS的非遞迴實現是非常類似的。下面我們也是同樣的分析一下實現過程:
還是從a到i,front指標指向隊頭。
- 先建立一個vector,用來儲存路徑,建立一個佇列將路徑裝入:
Vector<Vertex *> startPath
startPath.add(a)
q.enqueue(startPath)
- 此時佇列中有元素a,已經訪問的集合為空:
- 接下來到達迴圈語句:
in while loop: //將佇列中的元素取出,放到當前路徑的變數中 curPath = q.dequeue() (path is a) //由於佇列中只有一個元素a,所以它的最後元素也是它本身 v = last element in curPath (v is a) //標記V mark v as visited //將所有的未標記的路徑入隊q中 enqueue all unvisited neighbor paths onto q
這裡注意,這裡最後一句入隊的是路徑而不是節點,標記的才是及誒單。執行完畢後,佇列跟訪問集的情況如下:
- 此時佇列不為空,直接從while迴圈開始。
in while loop:
curPath = q.dequeue() (path is ab)
v = last element in curPath (v is b)
mark v as visited
enqueue all unvisited neighbor paths onto q
根據佇列的性質,FILO,所以最先出佇列的是ab,ab路徑中最後的元素是b,所以,標記b,並將b的所有鄰居放入佇列中去:
5.重複上述過程,直到路徑中的最後的元素為目標元素 i 時,停止搜尋。此時情況如下:
in while loop:
curPath = q.dequeue() (path is adhi)
v = last element in curPath (v is i)
found!
此時的路徑 a -> d-> h ->i 也是最短的路徑
總結
在對於需要找到最短路徑的圖來說,BFS是絕佳的選擇。廣度優先搜尋將查詢從起始節點可到達的所有節點。
●它將按距離遞增的順序訪問它們。
●在n節點m邊圖中,需要時間O(m + n)並使用空間O(n)。
●但在實踐中,空間使用率遠高於DFS。
這裡有個有趣的問題,如果我們將BFS思想運用在樹的遍歷中會得到什麼結論?得到一種我們熟悉的遍歷方式。樹中可是沒有環的。