如何計算圖的最短路徑?

算法導論(MIT 6.006 第15講 第16講 第17講)算法

最短路徑的定義是什麼?

最短路徑即擁有最小權重的路徑p;
路徑定義: p=<$v_0$,$v_0$,...,$v_k$>, 其中當$0\leq i<k$時,有 ($v_i$,$v_{i+1}$) $\in$ E;
路徑的權重:w(p)=$\Sigma{^{k-1}_{i=0}}w(v_i,v_{i+1})$ ;網絡

加上權重的數學表示方式

  1. 邊存在權重的圖:G(V,E,W) ,W是一個函數,做用於邊,生成一個實數,即W(E)->R
  2. 頂點到自身的路徑:($V_0$)表示從($V_0$)到($V_0$)的路徑,權重是0
  3. 兩個頂點之間的最短路徑:
    $\delta(u,v)=\lbrace{^{min\lbrace w(p)\rbrace{\quad} u,v之間存在路徑}_{\infty{\qquad}u,v以前不存在路徑}} $
E與V的關係 E=O($V^2$ )。對於有向圖來說,假設有兩個頂點,v1,v2,他們之間只有4種鏈接狀況,依次類推

爲何會有負的權重?

好比社交網絡上的喜歡能夠看作是正的權重,比喜歡能夠看作是負的權重函數

負權重的邊帶來什麼問題?

若是存在一個帶有負權重的邊,那麼每通過一個循環,會減小原有的權重值,這樣形成的現象是能夠獲得任何能夠獲得的權重值。好比路徑p=<S,A>權重是4,可是路徑p=<S,A,C,B,A>權重是3spa

圖片描述

最短路徑算法的通常思路是什麼?

d(v) 表示從源點s到當前節點v的路徑權重 ,$\pi[v]$表示當前最好的路徑上,v的前一個節點 ,經過這種方式就能重構整個最短路徑

針對沒有負權重的環code

  1. 初始化 d[v] = $\infty$ ,$\pi[u]$=NIL,d[s]=0
  2. 經過某種方式選擇邊(u,v),執行Relax操做,去更新源點到選擇的頂點的當前路徑值,以及選擇頂點的前一個節點
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)

relax操做的過程當中會不會產生一個一個比 $\delta$(s,v)還要小的值?

經過概括法,假設有 d[u] $\geq$ $\delta$(s,u)。已知的是$\delta(s,v)$表示s到v的最短路徑,那麼任意一個到v的頂點u和源點s到u的最短路徑一定大於等於$\delta(s,v)$,也就是
$$\delta(s,v)\leq\delta(s,u)+w(u,v)$$
經過前面的假設,則一定有 $\delta(s,v)\leq d[u]+w(u,v)=d[v]$ 。這說明,中間的過程的任意一個階段產生的結果d[v]都不會比$\delta$(s,v)還要小排序

最短路徑算法的通常思路問題一:錯誤的選邊致使複雜度爲指數級別

構造以下結構的圖隊列

邊的權值按照$2^{n/2}$方式分配,圖中給出的6個點的示例,若是所有顯示的邊($v_0$,$v_2$)的權值爲$2^{n/2}$,並依次遞減到1

圖片描述
假設源點爲$v_0$,初始化選擇的路徑以下,能夠獲得從源點到各個點的路徑長度。圖片

圖片描述

此時,Relax($v_4$,$v_6$)的邊,會更新$v_0$到$v_6$的路徑長度爲13ci

圖片描述
再Relax($v_2$,$v_4$)的邊,會更新$v_0$到$v_4$的路徑長度爲10get

圖片描述
因爲新$v_0$到$v_4$的路徑長度變短,那麼($v_0$,$v_5$)的路徑會變短爲11

圖片描述
這個時候有可能先選的執行Relax的邊是 ($v_5$,$v_6$),那麼($v_0$,$v_6$)的路徑會變短爲12

圖片描述
再次Relax邊($v_4$,$v_6$),那麼($v_0$,$v_6$)的路徑會變短爲11

圖片描述
針對有n個頂點的狀況:

  • 首先Relax($v_{n-2}$,$v_{n}$),使得d[$v_n$]減1
  • 再Relax($v_{n-4}$,$v_{n-2}$),使得d[$v_{n-2}$]減2
  • 而後Relax($v_{n-2}$,$v_{n-1}$)和($v_{n-1}$,$v_{n}$)使得d[$v_n$]減1
  • 再執行Relax($v_{n-2}$,$v_{n}$),使得d[$v_n$]減1

可發現,當Relax的邊($v_{n-2}$,$v_{n}$)權重爲1的時候,使得頂點d($v_n$)減1;當Relax邊($v_{n-4}$,$v_{n-2}$)權重爲2的時候,使得頂點d($v_n$)減2,也就是從權重按照 1,2,4,...,$2^{(n/2)-1}$,$2^{(n/2)}$的方式執行的過程當中,d($v_n$)須要執行減小的總次數爲1+2+4+...+$2^{(n/2)}$=$2^{(n/2)}-1$,也就是說,會執行的次數爲指數級別

最短路徑算法的通常思路問題二:負權重環

若是在源點到目標節點通過的路徑上,通過環會致使權重減小,這個算法不會結束

如何獲取有向無環圖(DAG)中,單個源點到某個點的最短路徑?

DAG表示只是沒有環,能夠存在負邊權重
  1. 對DAG進行拓撲排序,這樣保證了u到v的路徑必定是u在v以前
  2. 找到源點,按照從左到右,DAG排列的順序,對通過的每一個頂點進行Relax操做,便獲得了源點到全部頂點的最短路徑

假設排序好的拓撲圖以下,對於初始化時,每一個源點到每一個節點的距離都認爲是 $\infty$

圖片描述
第一步從源點往下走,找到它的全部的邊,對邊執行Relax操做

圖片描述
源點執行完畢,而後按照拓撲排列的順序,從左往右執行,因爲Relax只更改小於的狀況,所以只有最後兩個節點的路徑值被更新

圖片描述
繼續往右執行Relax

圖片描述

繼續往右執行Relax

圖片描述

至此執行完畢,能夠看到源點到全部節點的最短路徑,從左到右分別是 $\infty$,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爲源點

圖片描述

  1. 進行初始化,從A到其它節點的距離都是$\infty$,即 S={} ,Q={A(0),B($\infty$),C($\infty$),D($\infty$),E($\infty$)};
  2. 獲取隊列中的最小值,此時是A自己,此時S={A(0)},而後進行一次Relax操做,即發現A能達到的頂點爲B,C,更新後隊列中的值爲 Q={B(10),C(3),D($\infty$),E($\infty$)};
  3. 獲取隊列中的最小值,此時是C,S={A(0),C(3)},對選擇的C作Relax,C能到達的節點爲B,D,E,相應隊列更新爲:Q={B(7),D(11),E(5)};
  4. 獲取隊列的最小值,此時是E,S={A(0),C(3),E(5)},對選擇的E作Relax,E能到的節點爲D,因爲比現有的D值要大,因此沒有更新,Q={B(7),D(11)};
  5. 獲取隊列的最小值,此時是B,此時S={A(0),C(3),E(5),B(7)},B能到達的只剩下D了,B到D獲得的值爲9,要小,更新Q={D(9)}
  6. 獲取隊列最小的值,此時是D,此時S={{A(0),C(3),E(5),B(7),D(9)},至此結束。
括號中的值表示路徑距離

Dijkstra算法的時間複雜度

全部的耗時操做包括:

  • 將全部的頂點插入優先級隊列中,耗時爲 $\theta(V)$;
  • 從優先級隊列中提取一個最小的值,耗時爲$\theta(V)$;
  • Relax操做對邊進行d值減小,耗時爲$\theta(E)$;

實現優先級隊列方式不一樣,耗時不一樣

  1. 使用Array。 提取最小值花銷:$\theta(V)$,Relax對d值進行減小$\theta(1)$,操做全部的隊列中的元素,那麼時間就是 $\theta(V*V+E*1)$=$\theta(V^2)$
  2. 使用最小堆。提取最小值花銷:$\theta(\lg V)$,減小key的花銷$\theta(\lg V)$,操做全部的隊列中的元素,那麼時間就是 $\theta(V*\lg V+E*\lg V)$
  3. 使用Fibonacci堆,提取最小值花銷:$\theta(\lg V)$,減小key的花銷$\theta(1)$,能到達$\theta(V*\lg V+E)$
最直觀的使用Dijkstra的感覺是:如下圖爲例: 圖片描述
假設綠色的點是源點,若是用這樣長度的繩子將各個節點鏈接起來,那麼拎起綠色的球,從上往下懸掛,那些蹦直的線相加就是源點到各個點的最短距離,好比綠色是源點,到其它點的最短距離分別是 7,12,18,22(顏色依次是紫色、藍色、黃色、紅色)

爲何Dijkstra不能處理負權重環的問題?

Dikstra不會去看已經處理好的節點,只會處理沒有看到的節點,若是已經處理的節點都是最小的值,再不存在負權重環的狀況下,是不會出現使得路徑變小的狀況。詳見:https://stackoverflow.com/que...

若是在源點到目標節點通過的路徑上,有通過環且會致使權重減小,怎麼處理最短路徑問題?

使用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]=$\delta(s,v)$),不然只是檢測出存在負權重的環

耗時分析

兩個for循環,分別爲V,E,因此時間複雜度就是O(VE)

爲何Bellman-Ford算法在不存在負權重環的狀況下可以計算最小路徑?

只須要證實,若是不存在負權重的環,那麼通過Bellman-Ford有d[v]=$\delta(s,v)$。

取一條擁有最少邊的最短路徑p=<$v_0$,$v_1$,...,$v_k$>,其中$v_0$爲s,$v_k$=v。 若是不存在負權重的環,那麼說明p是一條簡單路徑,這代表,k$\leq$|V|-1。

這裏也不多是一個正環,即每通過這個環,權重增長,若是是那麼它就不是最短路徑了

當進行第一次循環的時候,取到的邊($v_0$,$v_1$)進行了Relax,那麼有 delta(s,$v_1$)=d[$v_1$]
進行第二次循環,取到的邊($v_1$,$v_2$)進行了Relax,那麼有delta(s,$v_2$)=d[$v_2$]
那麼通過k輪循環以後,有delta(s,$v_k$)=d[$v_k$],也就是說通過了|V|-1輪循環以後,每一個從源點可達的頂點都計算了最短路徑

簡單路徑(simple path):指除了起點和終點以外,其它頂點不會重複。對於簡單路徑p=<$v_0$,$v_1$,...,$v_k$>來說,若是k>=|V|,那麼路徑上總的頂點數是|V|+1,但實際只有 |V|個頂點,那麼一定存在一條重複的邊,使得非起點終點重複了,也就是說他不是簡單路徑了

爲何Bellman-Ford算法能檢測負權重環?

通過|V|-1輪循環以後,若是還有一條邊可以Relax,那麼當前從s到v的最短路徑並非簡單路徑,由於全部的節點都已經看過了,這時候確定存在了重複的節點,也就是說存在一個負權重的環

若是對一個路徑上有環,且全部權重值都是負權重,那麼使用Bellman-Ford算法能獲得最長路徑嗎?

不能,由於Bellman-Ford對於存在負權重的環的時候只會拋出異常,並無計算路徑,這實際是一個N-P的問題,即花的時間在指數級別或者之上

相似的,若是要求不通過負權重的環的狀況下,計算最短路徑,也並非件容易的事情
相關文章
相關標籤/搜索