關於最短路徑算法的理解

“最短路徑算法:Dijkstra算法,Bellman-Ford算法,Floyd算法和SPFA算法等。​從某頂點出發,沿圖的邊到達另一頂點所經過的路徑中,各邊上權值之和最小的一條路徑叫做最短路徑。”

我們解決最短路徑問題,常用的是Dijkstra與Floyd算法

Dijkstra(迪傑斯特拉)算法

他的算法思想是按路徑長度遞增的次序一步一步併入來求取,是貪心算法的一個應用,用來解決單源點到其餘頂點的最短路徑問題。

算法思想

首先,我們引入一個輔助向量D,它的每個分量D[i]表示當前找到的從起始節點v到終點節點vi的最短路徑的長度。它的初始態爲:若從節點v到節點vi有弧,則D[i]爲弧上的權值,否則D[i]爲∞,顯然,長度爲D[j] = Min{D[i] | vi ∈V}的路徑就是從v出發最短的一條路徑,路徑爲(v, vi)。
那麼,下一條長度次短的最短路徑是哪一條呢?假設次短路徑的終點是vk,則可想而知,這條路徑或者是(v, vk)或者是(v, vj, vk)。它的長度或者是從v到vk的弧上的權值,或者是D[j]和從vj到vk的權值之和。

一般情況下,假設S爲已知求得的最短路徑的終點集合,則可證明:一下條最短路徑(設其終點爲x)或者是弧(v, x)或者是中間只經過S中的頂點而最後到達頂點x的路徑。這可用反證法來證明,假設此路徑上有一個頂點不在S中,則說明存在一條終點不在S中而長度比此路徑短的路徑。但是這是不可能的。因爲,我們是按路徑常度的遞增次序來產生個最短路徑的,故長度比此路徑端的所有路徑均已產生,他們的終點必定在S集合中,即假設不成立。

因此下一條次短的最短路徑的長度是:D[j] = Min{D[i] | vi ∈ V - S},其中,D[i]或者是弧(v, vi)的權值,或者是D[k](vk ∈ S)和弧(vk, vi)上權值之和。

算法描述

假設現要求取如下示例圖所示的頂點V0與其餘各頂點的最短路徑:

在這裏插入圖片描述
我們使用Guava的ValueGraph作爲該圖的數據結構,每個頂點對應一個visited變量來表示節點是在V中還是在S中,初始時S中只有頂點V0。然後從nodes集合中遍歷找出從V0出發到各節點路徑最短的節點,並將該節點併入S中(即修改該節點的visited屬性爲true),此時就找到了一個頂點的最短路徑。然後,我們看看新加入的頂點是否可以到達其他頂點,並且看看通過該頂點到達其他點的路徑長度是否比從V0直接到達更短,如果是,則修改這些頂點的權值(即if (D[j] + arcs[j][k] < D[k]) then D[k] = D[j] + arcs[j][k])。然後又從{V - S}中找最小值,重複上述動作,直到所有頂點都併入S中。

Floyd(弗洛伊德)算法

Floyd算法是一個經典的動態規劃算法。是解決任意兩點間的最短路徑(稱爲多源最短路徑問題)的一種算法,可以正確處理有向圖或負權的最短路徑問題。(動態規劃算法是通過拆分問題規模,並定義問題狀態與狀態的關係,使得問題能夠以遞推(分治)的方式去解決,最終合併各個拆分的小問題的解爲整個問題的解。)

算法思想

從任意節點i到任意節點j的最短路徑不外乎2種可能:1)直接從節點i到節點j,2)從節點i經過若干個節點k到節點j。所以,我們假設arcs(i,j)爲節點i到節點j的最短路徑的距離,對於每一個節點k,我們檢查arcs(i,k) + arcs(k,j) < arcs(i,j)是否成立,如果成立,證明從節點i到節點k再到節點j的路徑比節點i直接到節點j的路徑短,我們便設置arcs(i,j) = arcs(i,k) + arcs(k,j),這樣一來,當我們遍歷完所有節點k,arcs(i,j)中記錄的便是節點i到節點j的最短路徑的距離。(由於動態規劃算法在執行過程中,需要保存大量的臨時狀態(即小問題的解),因此它天生適用於用矩陣來作爲其數據結構,因此在本算法中,我們將不使用Guava-Graph結構,而採用鄰接矩陣來作爲本例的數據結構)

算法分析及描述

假設現要求取如下示例圖所示任意兩點之間的最短路徑:

在這裏插入圖片描述
我們以一個4x4的鄰接矩陣(二維數組arcs[ ][ ])作爲圖的數據結構。比如1號節點到2號節點的路徑的權值爲2,則arcs[1][2] = 2,2號節點無法直接到達4號節點,則arcs[2][4] = ∞(Integer.MAX_VALUE),則可構造如下矩陣:

在這裏插入圖片描述
有向圖的初始鄰接矩陣

根據以往的經驗,如果要讓任意兩個頂點(假設從頂點a到頂點b)之間的距離變得更短,唯一的選擇就是引入第三個頂點(頂點k),並通過頂點k中轉(a -> k ->b)纔可能縮短頂點a到頂點b之間的距離。於是,現在的問題便分解爲:求取某一個點k,使得經過中轉節點k後,使得兩點之間的距離可能變短,且還可能需要中轉兩個或者多個節點才能使兩點之間的距離變短。比如圖中的4號節點到3號節點(4 -> 3)的距離原本是12(arcs[4][3] = 12),如果在只通過1號節點時中轉時(4 -> 1 ->3),距離將縮短爲11(arcs[4][1] + arcs[1][3] = 5 + 6 = 11)。其實1號節點到3號節點也可以通過2號節點中轉,使得1號到3號節點的路程縮短爲5(arcs[1][2] + arcs[2][3] = 2 + 3 = 5),所以如果同時經過1號和2號兩個節點中轉的話,從4號節點到3號節點的距離會進一步縮短爲10。於是,延伸到一般問題:
1、當不經過任意第三節點時,其最短路徑爲初始路徑,即上圖中的鄰接矩陣所示。
2、當只允許經過1號節點時,求兩點之間的最短路徑該如何求呢?只需判斷arcs[i][1]+arcs[1][j]是否比arcs[i][j]要小即可。arcs[i][j]表示的是從i號頂點到j號頂點之間的距離,arcs[i][1] + arcs[1][j]表示的是從i號頂點先到1號頂點,再從1號頂點到j號頂點的路程之和。循環遍歷一遍二維數組,便可以獲取在僅僅經過1號節點時的最短距離。

總結

1.Dijkstra算法是計算圖中的一個點到其它點的最小路徑.
  算法思路: 貪心算法.
    將圖中所有點分成 S(已求出解)U(未求出解)2個點集.dist[i]表示v0到v[i]當前已求得得最短路徑.A[n][n]爲邊集
    1.從剩下的邊集合中選出dist最短的邊並將邊的另一頂點vi從U中加入S.
    2.更新與vi連接的所有且並未在S中的點的dist矩陣值,dist[vk]=min(dist[vk],dist[vi]+A(i,k)).
    3.重複上述操作直到U中無與S中的點相連的點.
2.Floyd算法計算圖中任意一對點的最短路徑.
  算法思路:  T(n)=O(n^3).
   動態規劃法: Dis(i,j) =min(Dis(i,j), Dis(i,k) + Dis(k,j)).
3.Dijkstra算法爲啥不能存在負數邊?

Dijkstra中S(已求出解)中的每一個點解即最短路徑是已求出的,若存在負數路徑,可能存在已求出的解不是最優解.

面試題

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章