這是圖算法的第五篇文章:圖解:最短路徑之如何理解「鬆弛」or「放鬆」?
最短路徑問題的目的是找到從一個頂點到達另外一個頂點的成本最小的路徑。最短路徑算法
被普遍地應用於解決各類複雜的問題,好比在地圖中尋找兩個地點之間的最短路徑,如何在網絡鏈接中爲路由器尋找最短的傳輸路徑等等。爲了實現最短路徑算法
,人們發明了一系列的算法,好比:Dijkstra算法
與Bellman-Ford算法
。可是這些算法都基於一個被稱爲放鬆的基本操做算法
relaxtion,有些人稱爲鬆弛,我就直接簡單翻譯爲放鬆了,別管怎麼叫,理解就行
在這篇文章中,我會詳細介紹放鬆操做,同時給出解決最短路徑問題的基本(通用)思想。數組
這篇文章的大綱是:微信
咱們接下來要討論的問題被稱爲單源最短路(Single-Source Shortest Path),通俗來說,就是給定一幅加權圖和一個特定的頂點s
,稱爲源
;咱們的目標是對於圖中任意一點v
,計算從源s
到達v
的最短路徑網絡
G=(V,E)是一個加權圖數據結構
s
做爲一個特殊的頂點——叫作源
目標:對於圖中任意一點v
,計算從源s
到達v
的最短路徑spa
咱們一塊兒來看一個例子:
翻譯
在這幅圖中,咱們取源s
,對於頂點A
,從s
到達A
的路徑只有一條SA,因此最短路徑就是SA
,最小權重爲1
;對於頂點B
,從s
到達B
的路徑有兩條:SB
與SAB
,顯然最短路徑是SAB
,最小權重爲1+1=2
。3d
對於下面這幅圖呢?code
咱們把最小權重寫在每一個頂點內部會獲得圖二,這就是咱們的目標!blog
如今,咱們就來一塊兒看一下放鬆這一個最基本最重要的操做吧!
對於一條從頂點u
指向頂點v
的邊u-->v
來講,若是知足 d[u]+w(u,v)<d[v],就更新d[v]
,使得d[v]=d[u]+w(u,v)
;這就是對邊uv
的一次放鬆操做;其中,w(u,v)表示邊的權重,d(u)表示頂點u到達源s的最短距離(目前已知)
如下圖爲例,經過此次放鬆,咱們有可能可以改進d[v]
!頂點 v
本來有一個最短路徑值d[v]
,它是在咱們沒有掌握足夠多的信息的狀況下作出的臨時判斷,d[v]
可能真的是最終的答案也可能不是。咱們就是經過對邊uv
進行放鬆操做來看一下能不能改進。若是d[u]+w(u,v)<d[v]
成立,也就意味着咱們找到了一條更近的路徑到達頂點v
,這條路徑是經過頂點u
的那條。因此,咱們就更新頂點v
儲存的值d[v]
,同時還要更新路徑信息,使得edgeTo[v]=u
上面就是放鬆的定義,它的實質就是判斷一個頂點能不能有更好的選擇,已知的最短路徑能不能更短;若是知足那個不等式,就說明咱們能夠找到一條更好的路徑,就更新它,改進它!
上面咱們談到的是對邊的放鬆,可是在實際的代碼實現中,咱們的操做是對一個頂點進行放鬆。這兒理解起來很天然,對一個頂點進行放鬆就是對全部從該頂點發出的邊進行放鬆的總和
在上圖中,咱們對頂點v
操做中,對它相鄰的三個邊進行放鬆,其實質就是在問相應邊對面的頂點————「你可以被改進(更短)嗎?」
在瞭解了放鬆這個操做以後,咱們就來看一下如何利用放鬆來求最短路徑,下文以一個很簡單的圖來舉例
咱們首先將全部的頂點的值d[v]
標記爲無窮大(由於咱們還不能到達這些頂點),源s
特殊處理標記爲0
,即d[s]=0
,由於源s距離自身的距離顯然爲0
接下來,咱們對頂點s
進行放鬆。也就是對每個從頂點s
發出去的邊進行放鬆,分別是SA
與SB
。先對SA
放鬆,由於d[s]+1<∞
,符合放鬆的條件,放鬆邊SA
同時使edgeTo[a]=s
,獲得圖二
而後對邊SB
放鬆,一樣由於d[s]+1<∞
,符合放鬆的條件,放鬆邊SB
,同時使edgeTo[b]=s
獲得圖三
很容易發現,圖三並非最終的答案:SAB
邊要比SB
邊短,而後,對頂點A
進行相同的放鬆操做,獲得圖四即爲最終的最短路徑,同時改變edgeTo[b]=a
。
圖五就是操做的所有流程:
最後,咱們能夠經過edgeTo[]數組
來獲得全部的最短路徑。好比根據edgeTo[b]=a
獲得頂點B
是從頂點A
過來的;而再根據edgeTo[a]=s
獲得頂點A
是從頂點S
處過來的,由此溯源了一條從S
到達B
的完整路徑!
在上面的篇幅咱們討論了使用放鬆操做得到最短路徑的一個簡單示例,咱們從前日後依次對頂點S和A進行放鬆。可是因爲咱們的示例實在是太簡單了,就沒有重視放鬆的順序!在這裏,我想說的是,對於一個比較大的圖(至少頂點再也不是簡簡單單的三個),邊的放鬆順序重要嗎?或者說不一樣的放鬆順序可以達到相同的目的(求最短路徑)嗎?答案是否認的!
咱們仍是以一個例子說明這件事情:順序很重要!
在圖中,若是你先放鬆頂點S
,再放鬆B
,C
最後放鬆頂點A
就會出現問題!你會發現,當放鬆完頂點B
後,d[c]=5
,最後對A
放鬆並不會影響d[c]=5
的事實;因此,最後咱們獲得到達C
的最短路徑的值爲5
,而後實際上並非這樣,從圖中不難看出,最小值是SABC
,爲4
!
或許從聽到我這個問題你就感受到順序是有要求的,如今更加證明了你的想法,記住:放鬆的順序很重要!
咱們隨後要討論的好幾種最短路徑算法,都是在研究
放鬆的順序!好比:
Dijkstra算法
如何決定邊的放鬆順序主要取決於圖的性質
【有環無環】【有無負權重邊】
接下來咱們就以無環加權圖爲例來看一下具體的實現過程。
算法:先對圖進行拓撲排序,而後按照拓撲順序放鬆頂點
我感受僞代碼更能體現思路,因此在下面給出了僞代碼,具體的代碼實現能夠看一下算法4
:
DAG-Shortest-Path(G, s) Topological Sort G ; For each v, set d(v) = 1 ; Set d(s) = 0 ; for (k = 1 to |V|) { v = kth vertex in topological order ; Relax all outgoing edges of v ; } return d ;
首先對原圖進行拓撲排序,獲得頂點的拓撲順序,見【圖一】
而後,將源頂點的距離初始化爲0
,其餘頂點的距離值初始化爲∞
,從左向右依次對每一個頂點放鬆
至此,按照拓撲順序放鬆了全部頂點,最短路徑也就求出來了。注意:該算法有兩個比較重要的性質:
參考:Understanding Edge Relaxation for Dijkstra’s Algorithm and Bellman-Ford Algorithm
碼字繪圖不易,若是以爲本文對你有幫助,還請不要白嫖,關注、點贊、在看都是對小超創做的一種承認!
歡迎你們關注個人公衆號:小超說 ,以後我會繼續創做算法與數據結構以及計算機基礎知識的文章。也能夠加我微信chao_hey(備註:職業-城市) ,咱們一塊兒交流,一塊兒進步!