單源最短路——Dijkstra算法


定義概覽

Dijkstra(迪傑斯特拉)算法是典型的單源最短路徑算法,用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴展,直到擴展到終點為止。

問題描述:在無向圖 G=(V,E) 中,假設每條邊 E[i] 的長度為 w[i],找到由頂點 V0 到其余各點的最短路徑。(單源最短路徑)

算法描述

1)算法思想:

設G=(V,E)是一個帶權有向圖,把圖中頂點集合V分成兩組,第一組為已求出最短路徑的頂點集合(用S表示,初始時S中只有一個源點,以后每求得一條最短路徑 , 就將加入到集合S中,直到全部頂點都加入到S中,算法就結束了),第二組為其余未確定最短路徑的頂點集合(用U表示),按最短路徑長度的遞增次序依次把第二組的頂點加入S中。在加入的過程中,總保持從源點v到S中各頂點的最短路徑長度不大於從源點v到U中任何頂點的最短路徑長度。此外,每個頂點對應一個距離,S中的頂點的距離就是從v到此頂點的最短路徑長度,U中的頂點的距離,是從v到此頂點只包括S中的頂點為中間頂點的當前最短路徑長度。

2)算法步驟:

a.初始時,S只包含源點,即S={v},v的距離為0。U包含除v外的其他頂點,即:U={其余頂點},若v與U中頂點u有邊,則<u,v>正常有權值,若u不是v的出邊鄰接點,則<u,v>權值為∞。

b.從U中選取一個距離v最小的頂點k,把k,加入S中(該選定的距離就是v到k的最短路徑長度)。

c.以k為新考慮的中間點,修改U中各頂點的距離;若從源點v到頂點u的距離(經過頂點k)比原來距離(不經過頂點k)短,則修改頂點u的距離值,修改后的距離值的頂點k的距離加上邊上的權。

d.重復步驟b和c直到所有頂點都包含在S中。

 

執行動畫過程如下圖

 

 

動圖太快可以看下面的例子:


  

 

重點需要理解這句拗口的”按最短路徑長度的遞增次序依次把第二組的頂點加入S中。在加入的過程中,總保持從源點vS中各頂點的最短路徑長度不大於從源點vU中任何頂點的最短路徑長度

 

實際上,Dijkstra 算法是一個排序過程,就上面的例子來說,是根據A到圖中其余點的最短路徑長度進行排序,路徑越短越先被找到,路徑越長越靠后才能被找到,要找A到F的最短路徑,我們依次找到了 
A –> C 的最短路徑 
A –> C –> B 的最短路徑 
A –> C –> D 的最短路徑 
A –> C –> E 的最短路徑 
A –> C –> D –> F 的最短路徑 

 

為什么Dijkstra 算法不適用於帶負權的圖? 
就上個例子來說,當把一個點選入集合S時,就意味着已經找到了從A到這個點的最短路徑,比如第二步,把C點選入集合S,這時已經找到A到C的最短路徑了,但是如果圖中存在負權邊,就不能再這樣說了。舉個例子,假設有一個點Z,Z只與A和C有連接,從A到Z的權為50,從Z到C的權為-49,現在A到C的最短路徑顯然是A –> Z –> C

再舉個例子:

 

在這個圖中,求從A到C的最短路,如果用Dijkstra根據貪心的思想,選擇與A最接近的點C,長度為7,以后不再變化。但是很明顯此圖最短路為5。歸結原因是Dijkstra采用貪心思想,不從整體考慮結果,只從當前情況考慮選擇最優。

4.代碼模板

 1 #include<stdio.h>
 2 #include<string.h>
 3 #define inf 0x3f3f3f3f
 4 int map[110][110],dis[110],visit[110];
 5 /*
 6 關於三個數組:map數組存的為點邊的信息,比如map[1][2]=3,表示1號點和2號點的距離為3
 7 dis數組存的為起始點與每個點的最短距離,比如dis[3]=5,表示起始點與3號點最短距離為5
 8 visit數組存的為0或者1,1表示已經走過這個點。
 9 */
10 int n,m;
11 int dijstra()
12 {
13     int i,j,pos=1,min,sum=0;
14     memset(visit,0,sizeof(visit));//初始化為.,表示開始都沒走過
15     for(i=1; i<=n; ++i)
16     {
17         dis[i]=map[1][i];
18     }
19     visit[1]=1;
20     dis[1]=0;
21     for(i=1; i<n; i++)
22     {
23         min=inf;
24         for(j=1; j<=n; ++j)
25         {
26             if(visit[j]==0&&min>dis[j])
27             {
28                 min=dis[j];
29                 pos=j;
30             }
31         }
32         visit[pos]=1;//表示這個點已經走過
33         for(j=1; j<=n; ++j)
34         {
35             if(visit[j]==0&&dis[j]>dis[pos]+map[pos][j])//更新dis的值
36                 dis[j]=dis[pos]+map[pos][j];
37         }
38     }
39     return dis[n];
40 }
41 int main()
42 {
43     int i,j;
44     while(~scanf("%d%d",&n,&m),n||m)//n表示n個點,m表示m條邊
45     {
46         for(i=1; i<=n; ++i)
47         {
48             for(j=1; j<=n; ++j)
49             {
50                 map[i][j]=inf;//開始時將每條邊賦為最大值
51             }
52         }
53         int a,b,c;
54         for(i=1; i<=m; ++i)
55         {
56             scanf("%d%d%d",&a,&b,&c);
57             if(c<map[a][b])//防止有重邊
58                 map[a][b]=map[b][a]=c;
59         }
60         int count=dijstra();
61         printf("%d\n",count);
62     }
63     return 0;
64 }

鄰接表實現:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<vector>
 4 #include<algorithm>
 5 #define INF 0x3f3f3f3f
 6 using namespace std;
 7 struct node
 8 {
 9     int end;///終點
10     int power;///權值
11 } t;
12 int n;///n為點數
13 vector<node>q[500001];///鄰接表儲存圖的信息
14 int dis[500001];///距離
15 int vis[500001];///標記數組
16 void Dijkstra(int start,int end)
17 {
18     int i,len,j,pos;
19     memset(vis,0,sizeof(vis));
20     for(i=0; i<=n; i++)
21     {
22         dis[i]=INF;
23     }
24     len=q[start].size();
25     for(i=0; i<len; i++)
26     {
27         if(q[start][i].power<dis[q[start][i].end])
28         {
29             dis[q[start][i].end]=q[start][i].power;
30         }
31     }///從起點開始的dis數組更新
32     vis[start]=1;
33     for(j=0; j<n-1; j++)
34     {
35         int pos,min=INF;
36         for(i=1; i<=n; i++)
37         {
38             if(vis[i]!=0&&dis[i]<min)
39             {
40                 min=dis[i];
41                 pos=i;///找到未訪問節點中權值最小的
42             }
43         }
44         vis[pos]=1;
45         len=q[pos].size();///再次更新dis數組
46         for(j=0; j<len; j++)
47         {
48             if(vis[q[pos][j].end]==0&&dis[q[pos][j].end]>q[pos][j].power+dis[pos])
49             {
50                 dis[q[pos][j].end] = q[pos][j].power+dis[pos];
51             }
52         }
53     }
54     printf("%d\n",dis[end]);
55 }
56 int main()
57 {
58     int m,i;
59     int begin,end,power;
60     int a,b;
61     while(scanf("%d%d",&n,&m)!=EOF)
62     {
63         for(i=0; i<=n; i++)
64         {
65             q[i].clear();///將victor數組清空
66         }
67         for(i=0; i<m; i++)
68         {
69             scanf("%d%d%d",&begin,&end,&power);///輸入
70             t.end=end;
71             t.power=power;
72             q[begin].push_back(t);
73             t.end=begin;///無向圖
74             t.power=power;
75             q[end].push_back(t);
76         }
77         scanf("%d%d",&a,&b);///輸入起點與終點
78         Dijkstra(a,b);
79     }
80     return 0;
81 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM