1. 程式人生 > >單源最短路徑算法——Bellman-ford算法和Dijkstra算法

單源最短路徑算法——Bellman-ford算法和Dijkstra算法

clu ima class 實驗 ini contain span aaaaaa none

BellMan-ford算法描述

1.初始化:將除源點外的所有頂點的最短距離估計值 dist[v] ← +∞, dist[s] ←0;
2.叠代求解:反復對邊集E中的每條邊進行松弛操作,使得頂點集V中的每個頂點v的最短距離估計值逐步逼近其最短距離;(運行|v|-1次)
3.檢驗負權回路:判斷邊集E中的每一條邊的兩個端點是否收斂。如果存在未收斂的頂點,則算法返回false,表明問題無解;否則算法返回true,並且從源點可達的頂點v的最短距離保存在 dist[v]中。

1 BELLMAN-FORD(G, w, s)
2 INITIALIZE-SINGLE-SOURCE(G,s)
3 for i =1 to |G.V|-1  //
實驗證明最多只需|V|-1次外層循環,|V|-1次結束後,若圖G中無負權回路,那麽s到其他所有頂點的最短路徑求得 4   for each edge (u, v)∈E   //算法核心,松弛每一條邊,維持三角不等式成立 5     RELAX(u,v,w) //對每一條邊進行松弛操作 6 for each edge (u, v)∈E    //進行完|V|-1次循環操作後,如果還能某條邊還能進行松弛,說明到某個點的最短路徑還未找到,那麽必定是存在負權回路,返回FALSE 7   if d[v] > d[u] + w(u, v) 8     return FALSE 9 return TRUE    //
若進行上面的松弛之後沒有返回,說明所有的d值都不會再改變了,那麽最短路徑權值完全找到,返回TRUE

舉例說明:

給定原點是s,初始化時候除了原點s之外,其他的都是無窮大的。因為有5個頂點,所以需要松弛的次數為5-1次

技術分享圖片

這裏我們按照邊<t,x>、<t,y>、<t,z>、<y,x>、<y,z>、<z,x>、<z,s>、<s,t>、<s,y>的順序進行變得松弛操作。

第一次按照上述邊進行松弛操作之後(實際上只對<s,t>、<s,y>進行操作)的結果為

技術分享圖片

第二次按照給定邊進行松弛操作之後:

技術分享圖片

第三次松弛操作之後:

技術分享圖片

最後一次松弛操作:

技術分享圖片

給出實力代碼

技術分享圖片
 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 
 5 #define INF 0xffff      //權值上限
 6 #define maxe 5000       //邊數上限
 7 #define maxn 100        //頂點數上限
 8 int n, m;       //頂點數、邊數
 9 int d[maxn];    //保存最短路徑權值的數組
10 int parent[maxn];  //每個頂點的前驅頂點,用以還原最短路徑樹
11 struct edge     //表述邊的結構體,因為要對每一條邊松弛
12 {
13     int u, v, w;    //u為邊起點,v為邊端點,w為邊權值,可以為負
14 }EG[maxe];
15 
16 bool Bellman_Ford(int s)    //計算從起點到所有頂點的
17 {
18     for(int i = 1; i <= n; i++)     //初始化操作d[EG[j].v] > d[EG[j].u]+EG[j].w
19     {
20         d[i] = INF;
21         parent[i] = -1;
22     }
23     d[s] = 0;
24     bool flag;      //標記,判斷d值是否更新,跳出外層循環的依據
25     for(int i = 1; i < n; i++)  //外層循環最多做n-1次
26     {
27         flag = false;   //初始為false,假設不需再更新
28         for(int j = 0; j < m; j++)  //對m條邊進行松弛操作,若有更新,flag記為true
29             if(d[EG[j].v] > d[EG[j].u]+EG[j].w)     //if d[v] > d[u] + w(u, v),更新d[v]
30             {
31                 d[EG[j].v] = d[EG[j].u]+EG[j].w;
32                 parent[EG[j].v] = EG[j].u;
33                 flag = true;
34             }
35         if(!flag) break; //若松弛完每條邊後,flag狀態不變,說明未發現更新,可直接跳出循環
36     }
37     for(int i = 0; i < m; i++)  //做完上述松弛後,如果還能松弛,說明存在負權回路,返回false
38         if(d[EG[i].v] > d[EG[i].u]+EG[i].w)
39             return false;
40     return true;    //不存在負權回路,返回true
41 }
42 
43 int main()
44 {
45     int st;
46     printf("請輸入n和m:\n");
47     scanf("%d%d", &n, &m);
48     printf("請輸入m條邊(u, v, w):\n");
49     for(int i = 0; i < m; i++)
50         scanf("%d%d%d", &EG[i].u, &EG[i].v, &EG[i].w);
51     printf("請輸入起點:");
52     scanf("%d", &st);
53     if(Bellman_Ford(st))
54     {
55         printf("不存在負權回路。\n");
56         printf("源頂點到各頂點的最短路徑權值為:\n");
57         for(int i = 1; i <= n; i++)
58             printf("%d ", d[i]);
59         printf("\n");
60     }
61 }
View Code

輸入測試用例

1 2 -1
1 3 4
2 3 3
2 4 2
2 5 2
4 2 1
4 3 5
5 4 -3

技術分享圖片技術分享圖片?

Dijkstra算法

 (1)該算法要求所有邊的權重均為非負值,即對於所有的邊(u,v)∈E,ω(u,v)≥0,—— 既不能有負權重的邊,更 不能 有負權重的環。算法在運行過程中維持的關鍵信息是一組結點集合S。從源結點s到該集合中每個節點之間的最短路徑已經被找到。算法重復的從結點集合V-S中選擇最短路徑估計最小的結點u,將u加入到集合S中,然後對所有從u發出的邊進行松弛操作。

  (2)算法設計思想如下:

  技術分享圖片

  (3)算法描述偽代碼如下:

  技術分享圖片

  (4)下面給出算法一些理解:

  上述算法第一行執行的是d值和π值的初始化,第2行將集合S初始化為一個空集。算法所維持的循環不變式為Q=V-S,不變式在算法的while循環中保持不變。

  第3行對最小優先隊列進行初始化,將所有的結點V都放在隊列裏面。此時的S=?。

  在執行while循環的時候,從集合Q=V-S中抽取結點u,第6行將該結點加入到結合S裏面,從而保持不變式成立。

  然後將從u結點出發的所有結點進行松弛操作。如果一條經過結點u的路徑能夠使得從源結點s到結點v的最短路徑權重比當前的估計值更小,就對v.d的數值和前驅結點進行更新操作。

  (5)下面是一個實例

  輸入G,源結點1,結點集合V=<1,2,3,4,5,6>

  ①從源結點開始,此時集合S中只有結點1,從1出發的結點有6、2,所以更新此時的dist[2]和dist[6]的數值

   技術分享圖片

  ②選擇dist[2]和dist[6]中最小的值,即dist[6]=3,將結點6加入到集合S中,然後從更新結點6出發的結點2、5、4的路徑值,分別更新為dist[2] = 5、dist[5] = 4、dist[4] = 9。

  技術分享圖片

  ③選擇dist[2] = 5、dist[5] = 4、dist[4] = 9中最小的值,即dist[5] = 4,將結點5加入到結合S中,然後更新從結點5出發的結點的值,由於沒有從結點5出發的結點,所以直接進行下一步

  技術分享圖片

  ④第三步中選擇dist[2] = 5,將結點2加入集合S中,然後更新從結點2出發的結點3、4的值,分別為dist[3] = 12、dist[4] = 9

  技術分享圖片

  ⑤從剩下的兩個結點3、4中選擇dist更小的結點4,將結點4加入集合S中,然後更新從結點4出發的結點3的值,判斷之後,發現dist[3]依舊還是12

  技術分享圖片

  ⑥最後一步,將結點3加入集合S中,此時以及更新完畢,找到了從結點1到達所有節點的最短路徑

   技術分享圖片

單源最短路徑算法——Bellman-ford算法和Dijkstra算法