算法導論(MIT 6.006 第15講 第16講 第17講)算法
最短路徑即擁有最小權重的路徑p;
路徑定義: p=<,,...,>, 其中當時,有 (,) E;
路徑的權重:w(p)= ;bash
E與V的關係 E=O( )。對於有向圖來說,假設有兩個頂點,v1,v2,他們之間只有4種鏈接狀況,依次類推網絡
好比社交網絡上的喜歡能夠看作是正的權重,比喜歡能夠看作是負的權重函數
若是存在一個帶有負權重的邊,那麼每通過一個循環,會減小原有的權重值,這樣形成的現象是能夠獲得任何能夠獲得的權重值。好比路徑p=<S,A>權重是4,可是路徑p=<S,A,C,B,A>權重是3spa
d(v) 表示從源點s到當前節點v的路徑權重 ,表示當前最好的路徑上,v的前一個節點 ,經過這種方式就能重構整個最短路徑.net
針對沒有負權重的環3d
Relax(u,v,w):
select edge(u,v):
if d[v]>d[u]+w(u,v):
d[v]=d[u]+w(u,v)
PI[v]=u
until all edges have d[v] <= d[u]+w(u,v)
複製代碼
經過概括法,假設有 d[u] (s,u)。已知的是表示s到v的最短路徑,那麼任意一個到v的頂點u和源點s到u的最短路徑一定大於等於,也就是code
經過前面的假設,則一定有 。這說明,中間的過程的任意一個階段產生的結果d[v]都不會比(s,v)還要小cdn
構造以下結構的圖blog
假設源點爲 ,初始化選擇的路徑以下,能夠獲得從源點到各個點的路徑長度。邊的權值按照方式分配,圖中給出的6個點的示例,若是所有顯示的邊(,)的權值爲,並依次遞減到1
此時,Relax(,)的邊,會更新到的路徑長度爲13
再Relax( , )的邊,會更新 到 的路徑長度爲10 因爲新 到 的路徑長度變短,那麼( , )的路徑會變短爲11 這個時候有可能先選的執行Relax的邊是 ( , ),那麼( , )的路徑會變短爲12 再次Relax邊( , ),那麼( , )的路徑會變短爲11 針對有n個頂點的狀況:可發現,當Relax的邊(,)權重爲1的時候,使得頂點d()減1;當Relax邊(,)權重爲2的時候,使得頂點d()減2,也就是從權重按照 1,2,4,...,,的方式執行的過程當中,d()須要執行減小的總次數爲1+2+4+...+=,也就是說,會執行的次數爲指數級別
若是在源點到目標節點通過的路徑上,通過環會致使權重減小,這個算法不會結束
DAG表示只是沒有環,能夠存在負邊權重
假設排序好的拓撲圖以下,對於初始化時,每一個源點到每一個節點的距離都認爲是
第一步從源點往下走,找到它的全部的邊,對邊執行Relax操做 源點執行完畢,而後按照拓撲排列的順序,從左往右執行,因爲Relax只更改小於的狀況,所以只有最後兩個節點的路徑值被更新 繼續往右執行Relax繼續往右執行Relax
至此執行完畢,能夠看到源點到全部節點的最短路徑,從左到右分別是 ,0,2,6,5,3
使用Dijkstra算法。僞代碼算法以下:
Dijkstra(G,w,s): //G是圖,w是權值,s是源點
Initialize(G,s) // 初始化,設置d[s]=0,其它都是無窮,以及PI
S <- {} //已知最短路徑的點的集合
Q <- V[G] //須要被處理的頂點,能夠看作是一個最小優先級隊列,根據d()值進行排序
while Q is not empty: //只要還有沒處理的節點
u <- Extract-Min(Q) //從節點中找出一個最小的路徑權重的節點,並從Q中移除
S <- S U {u} //將找到的節點併到S中
for each vertex v belong to Adj
Relax(u,v,w) //對邊的d()值進行更新
複製代碼
例子以下,選擇A爲源點
括號中的值表示路徑距離
全部的耗時操做包括:
最直觀的使用Dijkstra的感覺是:如下圖爲例:
假設綠色的點是源點,若是用這樣長度的繩子將各個節點鏈接起來,那麼拎起綠色的球,從上往下懸掛,那些蹦直的線相加就是源點到各個點的最短距離,好比綠色是源點,到其它點的最短距離分別是 7,12,18,22(顏色依次是紫色、藍色、黃色、紅色)
Dikstra不會去看已經處理好的節點,只會處理沒有看到的節點,若是已經處理的節點都是最小的值,再不存在負權重環的狀況下,是不會出現使得路徑變小的狀況。詳見:stackoverflow.com/questions/6…
使用Bellman-Ford算法。
Bellman-Ford(G,w,s):
Initialuze(G,s)
for i=1 to |V|-1:
for each edge(u,v) belong to E:
Relax(u,v,w)
for each edge (u,v) belong to E:
if d[v]>d[u]+w(u,v)
report negative cycle exist
複製代碼
Bellman-Ford最終提供的是,若是沒有負權重的環,那麼能返回最短路徑(d[v]=),不然只是檢測出存在負權重的環
兩個for循環,分別爲V,E,因此時間複雜度就是O(VE)
只須要證實,若是不存在負權重的環,那麼通過Bellman-Ford有d[v]=。
取一條擁有最少邊的最短路徑p=<,,...,>,其中爲s,=v。 若是不存在負權重的環,那麼說明p是一條簡單路徑,這代表,k|V|-1。
這裏也不多是一個正環,即每通過這個環,權重增長,若是是那麼它就不是最短路徑了
當進行第一次循環的時候,取到的邊(,)進行了Relax,那麼有 進行第二次循環,取到的邊(,)進行了Relax,那麼有
那麼通過k輪循環以後,有,也就是說通過了|V|-1輪循環以後,每一個從源點可達的頂點都計算了最短路徑
簡單路徑(simple path):指除了起點和終點以外,其它頂點不會重複。對於簡單路徑p=<,,...,>來說,若是k>=|V|,那麼路徑上總的頂點數是|V|+1,但實際只有 |V|個頂點,那麼一定存在一條重複的邊,使得非起點終點重複了,也就是說他不是簡單路徑了
通過|V|-1輪循環以後,若是還有一條邊可以Relax,那麼當前從s到v的最短路徑並非簡單路徑,由於全部的節點都已經看過了,這時候確定存在了重複的節點,也就是說存在一個負權重的環
不能,由於Bellman-Ford對於存在負權重的環的時候只會拋出異常,並無計算路徑,這實際是一個N-P的問題,即花的時間在指數級別或者之上
相似的,若是要求不通過負權重的環的狀況下,計算最短路徑,也並非件容易的事情