圖——圖的Dijkstra法最短路徑實現

1,最短路徑的概念:ios

       1,從有向圖中某一頂點(起始頂點)到達另外一頂點(終止頂點)的路徑中,其權值之和最小的路徑;算法

      

2,問題的提法:數組

 

       1,給定一個帶權有向圖 G 與起始頂點 v,求從 v 到 G 中其它頂點的最短路徑(每條邊上都存在有意義的權值);數據結構

       2,Dijkstra 算法核心是經過已知最短路徑尋找未知最短路徑;測試

      

3,解決思路:spa

       1,Dijkstra 提出按路徑長度的遞增次序,逐步產生最短路徑;3d

              1,首先求出長度最短的一條最短路徑,再參照它求出長度次短的一條最短路徑,依次類推,直到從起始頂點 v 到其它各頂點的最短路徑所有求出爲止;code

       2,核心是經過遞歸的方式將從起始頂點到其餘各頂點的最短路徑所有求出來;blog

      

4,準備工做:遞歸

       1,輔助數組:Array<E> dist;

              1,dist[i] 表示當前從起始頂點 v0 到頂點 vi 的路徑長度;

       2,初始設置:

              1,若從起始頂點 v0 到頂點 vi 有邊:dist[i] 爲該邊上的權值;

              2,若從起始頂點 v0 到頂點 vi 無邊:dist[i] 爲無窮大;

             

5,Dijkstra 算法演示:

 

       1,由非空頂點集和邊集這兩個圖的基本定義可知,集合在圖的算法分析中的重要性,而集合在數據結構中常表現爲數組;

       2,每次關注的是剛剛加入 S 集合的頂點到其餘頂點的鏈接,經過剛剛求得的最短路徑值來求出抵達其餘頂點的可能的最短路徑值,這就是核心;

         

6,Dijkstra 算法步驟:

 

7,Dijkstra 算法精髓:

       1,S 集合內的頂點是已經找到最短路徑的頂點;

       2,v0 到 w 的最短路徑只能經過 S 集內的頂點;

       3,dist[w] 可能改變:

 

             

8,如何記錄最短路徑上的各個頂點?

       1,定義輔助數組:

              1,Array<int> path;

                     1,path[i] 表示當前路徑上的頂點 i 的前驅頂點;

                     2,初始化:path = {-1};

                     3,修改:

                           

                          

9,Dijkstra 算法流程圖:

 

10,Dijkstra 最短路徑算法:

 1    /* 兩個頂點之間最短路徑,返回的數組表示兩個最短路徑上面的頂點 */
 2     SharedPointer< Array<int> > dijkstra(int i, int j, const E& LIMIT)  // O(n*n)
 3     {
 4         LinkQueue<int> ret;  // 保存最短路徑上面的頂點
 5 
 6         if( (0 <= i) && (i < vCount()) && (0 <= j) && (j < vCount()) )
 7         {
 8             DynamicArray<E> dist(vCount());  // 用於存儲路徑值
 9             DynamicArray<int> path(vCount());  // 用於存儲當前結點的前驅結點
10             DynamicArray<bool> mark(vCount());  // 標記頂點是否進入 S 集合
11 
12             /* 原材料初始化 */
13             for(int k=0; k<vCount(); k++)
14             {
15                 mark[k] = false;  // 頂點都沒在 S 集合中
16                 path[k] = -1;  // 路徑沒有頂點前驅
17 
18                 dist[k] = isAdjacent(i, k) ? (path[k]=i,getEdge(i, k)) : LIMIT;  // 若是初始結點和其餘結點有鏈接,則設置爲邊上的權值,不然味無窮大;同時也要更新 path 數組,逗號表達式當前頂點 k 的前驅頂點是 i
20             }
21                                                                  
22             mark[i] = true;  // 將起始頂點放入 S 集合中
23 
24             /* 大循環 */
25             for(int k=0; k<vCount(); k++)
26             {
27                 E m = LIMIT;
28                 int u = -1;
29 
30                 /* 遍歷 dist 數組,從非S集合中找到到S 集合中頂點的最小路徑值 */
31                 for(int w=0; w<vCount(); w++)
32                 {
33                     if( !mark[w] && (dist[w] < m) )  // 沒有被標記的非 S 集中的點找最小值,更新小的值就是找最小路徑值
34                     {
35                         m = dist[w];  // 找到了最小路徑值
36                         u = w;  // 最短路徑記錄的頂點
37                     }
38                 }
39 
40                 /* 判斷是否有最小路徑被找到,防止只有頂點沒有邊 */
41                 if( u == -1 )
42                 {
43                     break;  // 只有頂點沒有邊,找不到兩點間最短路徑,由於此時 dist 數組都是理論最大值,找不到最小值,直接跳出
44                 }
45 
46                 mark[u] = true;  // 在非 S 集中找到了頂點 u 對應的路徑值是最小值,放入 S 集合
47 
48                 /* 算法核心:經過已知路徑,推出 S 集合到非 S 集合頂點最短路徑,由新加入的 u 來推進;這裏的 w 是非 S 集合中的點,對其遍歷;全部頂點都進入 S 集合中的時候,算法結束*/
49                 for(int w=0; w<vCount(); w++)
50                 {
51                     /* 以起始頂點 i 到 u 頂點的最短路徑爲基礎,來計算經過已知最短路徑所獲得的到其它頂點路徑是否最小,若是更小,則更新 dist 數組,這裏的 w 是最小權值頂點 u 的鄰接頂點,只要更小,就要更新 */
52                     if( !mark[w] && isAdjacent(u, w) && (dist[u] + getEdge(u, w) < dist[w]) )
53                     {
54                         dist[w] = dist[u] + getEdge(u, w);
55                         path[w] = u;  // 本次前驅頂點都是頂點 u 所表明的頂點
56                     }
57                 }
58             }
59 
60             LinkStack<int> s;
61 
62             s.push(j);  // 終止頂點 j 先放入棧中;
63 
64             /* 將從起始頂點 i 到終值頂點 j 的點先放到棧中去;前驅結點的訪問方式、值當作位置;值就是前面的頂點,因此直接把值壓入進棧*/
65             for(int k=path[j]; k!=-1; k=path[k])
66             {
67                 s.push(k);
68             }
69 
70             /* path 中保存的頂點順序是逆序的,經過棧中轉下,調整過來; */
71             while( s.size() > 0 )
72             {
73                 ret.add(s.top());
74 
75                 s.pop();
76             }
77         }
78         else
79         {
80             THROW_EXCEPTION(InvalidParameterException, "Index <i, j> is invalid ...");
81         }
82 
83         /* 最終最短路徑經歷頂點數至少有 2 個,不然 i 到 j 是不可達的,最多多少頂點是不知道的 */
84         if( ret.length() < 2 )
85         {
86             THROW_EXCEPTION(ArithmeticException, "There is no path from i to j ...");
87         }
88 
89         return toArray(ret);  // 放到數組裏面
90     }

 

11,Dijkstra 最短路徑算法測試代碼:

 1 #include <iostream>
 2 #include "MatrixGraph.h"
 3 #include "ListGraph.h"
 4 
 5 using namespace std;
 6 using namespace DTLib;
 7 
 8 template< typename V, typename E >
 9 Graph<V, E>& GraphEasy()
10 {
11    static MatrixGraph<4, V, E> g;
12 
13     g.setEdge(0, 1, 1);
14     g.setEdge(0, 2, 3);
15     g.setEdge(1, 2, 1);
16     g.setEdge(1, 3, 4);
17    g.setEdge(2, 3, 1);
18 
19     return g;
20 }
21 
22 template< typename V, typename E >
23 Graph<V, E>& GraphComplex()
24 {
25    static ListGraph<V, E> g(5);
26 
27     g.setEdge(0, 1, 10);
28     g.setEdge(0, 3, 30);
29     g.setEdge(0, 4, 100);
30     g.setEdge(1, 2, 50);
31     g.setEdge(2, 4, 10);
32     g.setEdge(3, 2, 20);
33    g.setEdge(3, 4, 60);
34 
35     return g;
36 }
37 
38 int main()
39 {
40     Graph<int, int>& g = GraphComplex<int, int>();
41    SharedPointer< Array<int> > p = g.dijkstra(0, 4, 65535);
42 
43     for(int i=0; i<p->length(); i++)
44     {
45         cout << (*p)[i] << " ";
46    }
47 
48    cout << endl;
49 
50     return 0;
51 }

 

12,小結:

       1,Dijkstra 最短路徑算法是基於遞推的思想完成的;

       2,起始頂點到其餘各頂點的最短路徑經過動態推導獲得;

       3,未標記頂點的最短路徑只能由已標記頂點計算得出;

       4,算法的最終結果是起始頂點到其它各頂點的最短路徑;

相關文章
相關標籤/搜索