廣度優先演算法,深度優先演算法和DijKstra演算法

2021-07-27 11:12:47 字數 3429 閱讀 9078

我們經常會碰到最短路徑問題,而最短路徑問題的解決方法多種多樣,廣度優先搜尋(bfs),深度優先搜尋(dfs)和dijkstra演算法貌似都能解決這個問題,這裡就簡單介紹一下這些演算法,分析一下它們的適用範圍。

一、原理剖析:

1 廣度優先搜尋(bfs)

廣度優先搜尋依賴的是佇列解決問題。佇列中的每乙個節點需要包含記錄以下內容:該節點到起點的距離dist,該節點的前驅節點past,該節點在當前路徑下是否被訪問過visit(0表示沒有訪問過,1表示當前路徑下正在訪問,2表示該節點周圍的所有節點都已經被訪問過)。

程式設計邏輯大致如下:

初始化:

起點值初始化(past=null,dist=0,visit=1)

其他節點值初始化(past=null,dist=無窮,visit=0)

起點入隊

迴圈1:直到佇列中沒有元素

從隊伍中輸出乙個節點作為當前節點

迴圈2:訪問與當前節點連通但是【沒有被訪問過】的節點(visit=0的節點)

將即將訪問的節點記為正在訪問的狀態

將即將訪問的節點的狀態更新(past=當前節點,dist=即將訪問的節點到當前節點的距離,visit=1)

即將訪問的節點入隊

將當前節點的visit記為2(因為與它連線的所有節點都被訪問過)

為什麼需要記錄這些內容?我們逐一解釋:首先,節點到起點的距離很好解釋,這是問題的需求,但是在二叉樹的層序遍歷時,我們僅僅需要將節點輸出,而不需要計算距離,此時不記錄這個內容也沒關係。如果題意不要求輸出最短的路徑,而是只要求我們記錄最短的路徑是多少,那不記錄前驅節點問題也不大,它不影響最短路徑的求解。記錄該節點在當前路徑下是否被訪問的目的是,避免圖中有環路而造成節點的重複訪問和死迴圈,但是對於樹形結構,當前節點不可能遍歷以前訪問過的節點,這個內容可以不記錄。綜上,在寫廣度優先演算法的**時,要依據需求變通,不應教條。

2 深度優先搜尋(dfs)

深度優先搜尋依賴的是遞迴,你完全可以把深度優先搜尋理解為動態規劃的一種形式。它一樣要記錄:該節點到起點的距離dist,該節點的前驅節點past,該節點在當前路徑下是否被訪問過visit(0表示沒有訪問過,1表示當前路徑下正在訪問,2表示該節點周圍的所有節點都已經被訪問過)。記錄這些值的目的與廣度優先搜尋也差不多。

程式設計邏輯大致如下:

初始化:

所有節點值初始化(past=null,dist=無窮,visit=0)

遞迴dfs(當前節點)

當前路徑正在訪問當前節點(visit=1)

對與當前節點連通的所有【沒有被訪問過】的節點

改變即將訪問的節點的狀態(past為當前節點,dist為即將訪問的節點到當前節點的距離)

dfs(即將訪問的節點)

【如果有環路,這裡還要加一步:如果當前節點的visit不是2,就把visit設為0,否則被訪問過一次就再也訪問不了了】

將當前節點的visit記為2(因為與它連線的所有節點都被訪問過)

3 dijkstra演算法

dijkstra演算法是運籌學中求最短路徑的常規演算法,它的中心思想是:讓每個節點記錄它到起點的最短路徑,其實也可以理解為動態規劃的一種形式。與前面兩種方法相同,dijkstra演算法也需要記錄:該節點到起點的距離dist,該節點的前驅節點past。

但是不同的是,不需要記錄該節點是否被訪問過,而是記錄:該節點到起點的最短距離是否已經確定visit(0表示還沒有確定了該節點到起點的最短距離,1表示已經確定該節點到起點的最短距離)。

程式設計邏輯大致如下:

初始化:

起點初始化(dist=0,past=null,visit=1)

其他節點初始化(dist=無窮,past=起點,visit=0)

迴圈:對於所有節點

迴圈:對於所有【不確定到起點最短距離】的節點,找出距離起點最近的節點並記錄距離

更新找出的節點的狀態(visit=1)

迴圈:對於所有【不確定到起點最短距離】的節點

計算它到剛才找出的節點的距離

如果節點經過剛才找出的節點到起點的距離小於節點直接到達起點的距離

更新節點的狀態(dist=找出的節點的dist+找出的節點到該節點的距離,past=找出的節點)

二、優缺點剖析:

首先我們看如下無向圖:假設所有邊的權重都是正數

如果我想求從1到5的最短距離,上述描述的三種方法都可以。

我們再看如下情況:

假設所有邊的權重都是相同的正數,求從1到3的最短路徑。

我們利用dfs依照節點的id從小到大搜尋,只能得到1-2-3這樣的結果,那是因為1-3這條路徑根本沒有被訪問到

我們再看演算法導論一書中給出的bfs和dfs示意圖:

上圖是bfs的示意圖,從圖中可以清楚地看出一次bfs的完整過程。我們可以發現:沒有加粗的邊是沒有被訪問過的。

上圖是dfs的示意圖,從圖中可以清楚地看出一次dfs的完整過程,我們可以發現:虛線邊是沒有被訪問過的。

到這裡結論已經很清楚了:

dfs和bfs是以遍歷所有節點為主要目的的演算法,他們不一定能遍歷所有的路徑。即使在一些三種方法通用的情況下,dfs和bfs還有一些程式設計上的繁瑣之處,主要表現在,我們需要額外定義變數存放最短路徑,節點自身攜帶的最短路徑和前驅節點是隨著迴圈/迭代的進行不斷更新的,它不能在節點中保留最優解。而對於dijkstra演算法而言,節點自身保留的就是最優情況,不需要額外定義變數。

綜上所述:我的建議是,針對樹形結構求解類似最短路徑或最小權重和的題目時,dfs和bfs是不錯的選擇,但是當遇到有環狀結構的最短路徑題時應該格外警惕,此時選擇dijkstra演算法比較穩妥。

深度優先dfs的應用:

樹的深度求解

dijkstra:

最短路徑

深度優先演算法和廣度優先演算法

圖形的深度優先搜尋法 void dfs int current 主程式 建立圖形後,將遍歷內容印出.void main 邊線陣列 int i for i 1 i 8 i creategraph node,20 建立圖形 printf 圖形的鄰接鍊錶內容 n for i 1 i 8 i printf ...

演算法 廣度優先演算法和深度優先演算法

廣度 bfs 和深度 dfs 優先演算法這倆個演算法是圖論裡面非常重要的兩個遍歷的方法。下面乙個例子迷宮計算,如下圖 解釋 所謂廣度,就是一層一層的,向下遍歷,層層堵截,看下面這幅圖,我們如果要是廣度優先遍歷的話,我們的結果是v1 v2 v3 v4 v5 v6 v7 v8。廣度優先搜尋的思想 訪問頂...

演算法 深度優先和廣度優先

深度優先搜尋屬於圖演算法的一種,是乙個針對圖和樹的遍歷演算法,英文縮寫為dfs depth first search 深度優先搜尋利用深度優先搜尋演算法可以產生目標圖的相應拓撲排序表,利用拓撲排序表可以方便的解決很多相關的圖論問題,如最大路徑問題等等。一般用堆資料結構來輔助實現dfs演算法。文字描述...