【數據結構】圖-最短路徑問題

最短路徑問題的抽象
·在網絡中,求兩個不同頂點之間的所有路徑中,邊的權值之和最小的那一條路徑
這條路徑就是兩點之間的最短路徑(shortest path)
第一個頂點爲源點(source)
最後一個頂點爲終點(destination)

問題分類:
單源最短路徑問題:從某固定源點出發,求其到所有其他頂點的最短路徑。
(有向)無權圖
(有向)有權圖
多源最短路徑問題:求任意兩頂點間的最短路徑

無權圖的單元最短路算法
按照遞增(非遞減)的順序找出到各個頂點的最短路
在這裏插入圖片描述
如圖,求v3到其餘各點的最短路
Step 1 : 直接從v3出發,能直接到達v1和v6,將其記錄,表示v3到這兩個點的路程爲1
Step 2 : 從step1中獲取到的兩點出發,先從v1出發,能直接到達v2和v4,即表示v3到這兩個點的路程爲1+1=2;從v6出發,發現v6並沒有箭頭指向其它點,結束
Step 3 : 同上 從v2出發,v4已經訪問過了,不訪問,可以訪問v5;從v4出發,v5已經訪問過,不訪問,可以訪問v7,那麼v5,v7就是從v3出發的兩點路程爲2+1=3的點
至此,所有點已經訪問完畢。

有沒有覺得和BFS很像

實際上代碼也和BFS差不多,但是有修改:
在這裏插入圖片描述
需要一個更加具體的數組來表示到每個點的最短路徑是多少,還可以順便準備一個數組,表示路徑
在這裏插入圖片描述
S是其本身,因此dist[S] = 0;其餘dist[]的初始值可以設置一個奇妙的值,比方說-1,比方說-9999等。
訪問到一個點之後,它的路徑就是我此時出發點的路徑+1
在這裏插入圖片描述
如果dist的初始值都設爲0,那麼在if判斷那邊就是if(dist[W] == 0),這樣源點會一直入隊出隊入隊出隊,進入死循環。
在這裏插入圖片描述
最後模擬結果如上,文字描述參考上文,path模擬方法類似不多贅述了。

有權圖的單元最短路算法
在這裏插入圖片描述
圖1,設權重指代通行費,想求v1到v6最省錢路線,可以得出v1-v4-v7-v6 = 6
圖2,同樣的,但v2->v5爲-10,此時想求v5到v4最省錢路線,則會進入v5-v4-v2-v5死循環(一時賺錢一時爽,一直賺錢一直爽),這個-10稱爲負值圈,在算法中一般都需要避免負值圈的出現。

Dijkstra算法
一個用於有權圖最短路徑的算法,直接貼出代碼和模擬:
在這裏插入圖片描述
在這裏插入圖片描述
尋找v1開始到各點的最短路徑:
準備工作:將v1的dist設爲0(自己),並設置標記(已錄入),再把v1相鄰的點錄入(v2,v4)
疑惑:v2的權重是2,但萬一有一條路徑是1呢?v4的路徑是1可以直接錄入,但是v2我保持疑問 如果真有這種情況,也會通過其它路徑走到,到時候更新v2就可以了)

代碼解釋:
collected[V] 代表的是這個點V是否已經作爲一個源點在四周查找鄰接點過了;
E<V,W> 表示帶權路徑<V,W>的權重值
dist[W] 距離源點的距離(最終結果都是最短距離)
path[W] 走到這個點的上一點是什麼(最終能通過path定位到數組下標,一直回溯就會回到起點,路徑數字化表現可以用堆棧實現)

進入函數:傳參vertex s爲頂點v1的下標(實質上就是int v),進入while循環
Step 1 : 從dist數組進行遍歷,尋找未收錄頂點中 dist最小者爲新的出發點,此時是v4。然後將其標記爲「已訪問」,開始遍歷v4的每個鄰接點(v3, v5, v6, v7),並且這些鄰接點都得是「未訪問過」 (如果已訪問過,表示這個點已經求出最短路了,再去訪問浪費時間)。如果v4的dist加上v4->vi的權重小於vi已保存的dist(這條路更近),那就更新vi的dist,並且更新vi的path爲v4,第一輪循環結果如下:
在這裏插入圖片描述
Step 2 : v4結束,繼續從dist數組中遍歷尋找新的出發點,易得爲v2,此時將v2作爲V,標記「已訪問」,再開始訪問v2的鄰接點。v4已訪問過,因此不考慮;v5未訪問過,但是dist[V]+E<v2,v5> = 12 > 3,因此不更新。v2循環結束。

Step 3 :這時該從v3開始,v3的鄰接點爲v6,此時dist[V] + E<v3,v6> = 8 < 9,因此更新dist[v6]和path[v6]。

以下類似不多贅述,最終更新結果爲:
在這裏插入圖片描述

舉例:求v1到v6的最短路的路徑
直接找到v6,再通過path[v6]能得出v6的上一點爲v7,找到v7
再通過path[v7]找到上一點爲v4,以此類推能得到一串數字:6-7-4-1,放入堆棧就能轉換爲1-4-7-6即路徑。

多元最短路算法
就是將每個點都作爲起點,然後都給求一次各點的最短路
在這裏插入圖片描述
在這裏插入圖片描述