演算法導論筆記 單源最短路徑

2021-09-22 01:35:28 字數 4172 閱讀 7472

本文所貼示的偽**均**《演算法導論》,本文只是對其中《單源最短路徑》章節的簡單總結,許多數學證明過程已忽略。

最短路徑的定義:給定乙個圖g=(v,e),希望找到從給定源節點s∈v 到每個結點v∈v 的最短路徑。

單源最短路徑可以用來解決許多其他問題,包括:

1、單目的地最短路徑問題:找到從每個結點v到給定目的結點t的最短路徑,如果將圖的每條邊的方向翻轉過來,就可以將這個問題轉換為單源最短路徑問題。

2、單結點對最短路徑問題:找到從給定結點u到給定結點v的最短路徑,如果解決了針對單個結點u的單源最短路徑問題,也就解決了這個問題。

3、所有結點對的最短路徑問題:對於每個結點對u和v,找到從u到v的最短路徑,雖然可以針對每乙個結點執行一次單源最短路徑演算法,但是通常可以更快地解決這個問題。

注:最短路徑演算法通常依賴最短路徑的乙個重要性質---兩個結點之間的一條最短路徑包含著其他最短路徑

為了討論單源最短路徑問題,這裡需要作一些必要的定義

最短路徑的表示:通常情況下,我們不僅希望計算出最短路徑的權重,還希望計算出最短路徑上的結點。給定圖g=(v,e),對於每個結點v,我們維持乙個前驅結點v.p,該前驅結點可以是另乙個結點或者nil。這裡的最短路徑演算法對每個結點的前驅進行設定,這樣,將從結點v開始的前驅結點反過來,也就是從s到v的最短路徑了。

最短路徑樹:最短路徑樹是一棵有根節點的樹,該樹包含了從源結點s到每個可以從s到達的結點的一條最短路徑。當然,最短路徑不是唯一的,最短路徑樹自然也不是唯一的。

鬆弛操作:本章演算法需要使用鬆弛(relaxation)技術。對於每個結點v來說,我們維持乙個屬性v.d。用來記錄從源結點s到結點v的最短路徑權重的上界,我們稱v.d為s到v的最短路徑估計。這裡使用下面的偽**來對最短路徑的估計和前驅結點進行初始化:

對一條邊(u,v)的鬆弛過程為:首先測試一下是否可以對從s到v的最短路徑進行改善。測試方法是,將從結點s到u之間的最短路徑距離加上與v之間的邊權重,並與當前的從s到v的最短路徑估計進行比較,如果前者更小,則對v.d和

最短路徑和鬆弛操作的性質:

1、三角不等式性質:對於任何邊(u,v)∈e,我們有δ(s,v)<=δ(s,u)+w(u,v);

2、上界性質:對於所有結點v∈v,總是有v.d>=δ(s,v)

3、非路徑性質:如果從結點s到v之間不存在路徑,則v.d=δ(s,v) =∞

4、路徑鬆弛性質:如果p是從源結點v0到結點vk的一條最短路徑,並且我們對p中所有邊的鬆弛次序為(v0,v1)(v1,v2)…則vk.d=δ(s,vk).(大概意思是,在執行了一系列的鬆弛操作後,所有結點都取得了最後的最短路徑權重,那麼前驅子圖將也會是一棵最短路徑樹)

5、收斂性質:對於某些結點u,v∈v,如果s~u->v是圖g中的一條最短路徑,並且在對邊(u,v)進行鬆弛前的任意時間有u.d=δ(s,u),則在此之後所有時間有v.d=δ(s,v);

bellman-ford演算法解決的是一般情況下的單源最短路徑問題。這裡,邊的權重可以為負值。bellman-ford演算法通過對邊進行鬆弛操作來漸進地降低從源結點到每個結點v的最短路徑估計值v.d,直到該估計值與實際的最短路徑權重δ(s,v)相同為止。其偽**如下:

此演算法對每條邊進行|v|-1次鬆弛操作(因為圖g中,對於任意源結點,其到達其他任意結點的無環路徑最多只有|v|-1條邊,所以對於任意一條邊也需要經過|v|-1次鬆弛以求出最小的估計值)

該演算法返回乙個布林值,以表明是否存在乙個從源結點可以到達的權重為負值的環路。如果存在這樣乙個環路,演算法將告訴我們不存在乙個解決方案。

根據結點的拓撲排序次序來對帶權重的有向無環圖g=(v,e)進行邊的鬆弛操作,我們便可以在o(v+e)的時間內計算出單個源結點到所有結點之間的最短路徑。在有向無環圖中,即使存在權重為負值的邊,但因為沒有權重為負值的環路,最段路徑都是存在的。

dijkstra演算法解決的是帶權重的有向圖上單源最短路徑問題,該演算法要求所有邊的權重都為非負。如果採用合理的實現方式,dijkstra演算法的執行時間要低於bellman-ford演算法的執行時間。

dijkstra演算法在執行過程中維持的關鍵資訊是一組結點集合s.從源結點s到該集合中的每乙個結點的最短路徑已被找到。演算法重複從集合v-s中選擇最短路徑估計最小的點u,將u加入到集合s。然後對所有從u發出的邊進行鬆弛。在下面的實現方式中,使用乙個最小優先佇列q來儲存結點集合,每個結點的關鍵字即為d值。(注:下圖左邊是dijkstra演算法。右邊的prim是用來對比的,因為兩者真的很相似!)

dijkstra演算法對邊的鬆弛如下所示(圖源《演算法導論》)

因為dijkstra演算法總是選擇集合v-s中「最輕」或者「最近」的結點來加入到s中,該演算法使用的是貪心策略。雖然貪心策略並不總是能得到最優結果,但是使用貪心策略的dijkstra演算法確實能計算出最短路徑。這裡的關鍵證明是乙個事實:該演算法每次選擇結點u來加入到s時,有u.d=δ(s,u);

dijkstra演算法的時間複雜度取決於最小優先順序佇列的實現。如果利用結點號1~|v|來維持最小優先佇列,時間複雜度為o(v^2+e)=o(v^2);

如果使用的是斐波那契數列,可以將該演算法的時間改善到o(vlo**+e).

dijkstra演算法既類似於廣度優先搜尋,也有點類似於計算最小生成樹的prim演算法。其與prim相似之處在於,二者都是用最小優先佇列來尋找給定集合之外「最輕」的結點,將該結點加入到集合裡,並對位於集合外面的結點權重進行相應調整。區別在於,prim演算法並不進行鬆弛操作,其每一步都只尋找輕量級邊,而dijkstra演算法選出優先順序佇列中權重最小的點後要進行鬆弛操作。

看到這裡,可能混淆了dijkstra和prim演算法的區別,這裡做個簡單的示例。比如下面這個三角形:

很顯然,假設源結點是s(圖上沒有畫出來,但是可以確定s通過某條路徑到達了a),目前把a加入到了集合內:

step1、這時候,prim和dijkstra都對a的鄰接邊進行處理;其中,prim演算法讓b和c的父親結點都先變為a,然後讓b和c的key都變為「父結點到本結點的權重」,分別為3和5;而dijkstra演算法則是對邊a-b,a-c都進行了鬆弛,分別更新了δ(s,b)=δ(s,a)+3和δ(s,c)=δ(s,a)+5,並讓b、c的父親結點都變成a.

step2、接下來,無論是dijkstra演算法還是prim演算法都會先選擇b加入集合,因為很顯然在兩個演算法中b此時都處於優先順序佇列的最前端。

step3、待b加入集合中後,分歧點就開始了:此時在prim演算法中,因為b-c的權重小於先前c中記錄的鍵值(先前記錄的是a-c的權重),所以這時候b就會成為c的父親結點了。而對於dijkstra演算法,c中記錄的是「最短路徑估計值」,也就是說,此時c中的key是δ(s,a)+5,而在鬆弛時,比較一下δ(s,b)+w(b,c)和δ(s,a)+5,顯然後者更小,所以c的父親結點與鍵值均不變!

所以區分兩者的關鍵在於,將某點加入到集合中後的後續操作:prim只是單純比較路徑權重,dijkstra卻是「最短路徑估計值」(鬆弛操作)!

也就是說,dijkstra記錄了累計的「消耗」,是「長視」的;而prim只關注當下橫跨切割的邊誰是最短的,而不關注從源結點過來的累加路徑是不是最短的,是「短視」的。

單源最短路徑演算法

簡單介紹 最短路徑演算法是圖演算法中的經典演算法,是用於解決圖中某個頂點到另外乙個頂點所經過路徑的花銷最小 這裡的花銷可能是時間也可能指費用等 dijkstra是用於解決單源最短路徑的經典演算法。圖的儲存方式 鄰接矩陣 在鄰接矩陣中,要獲取某個結點的出度和入讀,是通過掃瞄結點所在臨界矩陣中的行列完成...

演算法導論小結 11 單源最短路徑問題

by 潘雲登 對於商業目的下對本文的任何行為需經作者同意。寫在前面 1.本文內容對應 演算法導論 第 2版 第 24章。2.主要介紹了兩種求解單源最短路徑的演算法 bellman ford 演算法和dijkstra 演算法。3.最短路徑問題 在最短路徑問題中,給出的是乙個帶權有向圖 g v,e 加權...

《演算法導論》第24章 單源最短路徑

單元最短路徑問題 單源最短路徑問題,試 決從乙個原點到圖中其他所有點的最短路徑問題。此問題的變體有單終點最短路徑問題,單終點最短路徑問題和每對頂點間最短路徑問題。可以相應理解為一對多 多對 一 一對一和多對多的對映關係。負權值邊和負權迴路 某些權值可以是負的,這些邊稱為是負權值邊。負權迴路指的是構成...