單源點最短路徑的Dijkstra算法


在帶權圖(網)里,點A到點B所有路徑中邊的權值之和為最短的那一條路徑,稱為A,B兩點之間的最短路徑;並稱路徑上的第一個頂點為源點(Source),最后一個頂點為終點(Destination)。在無權圖中,最短路徑則是兩點之間經歷的邊數最少的路徑。實際上,只要把無權圖上的每條邊都看成是權值為1的邊,那么無權圖和帶權圖的最短路徑是一致的。
   給定一個帶權有向圖G=(V,E),指定圖G中的某一個頂點的V為源點,求出從V到其他各頂點之間的最短路徑,這個問題稱為單源點最短路徑問題。
   迪傑斯特拉(Dijkstra)根據若按長度遞增的次序生成從源點v0到其它頂點的最短路徑,則當前正在生成的最短路徑上除終點外,其余頂點的最短路徑均已生成的這一思想,提出了按路徑長度遞增的次序產生最短路徑的算法(在此,路徑長度為路徑上邊和弧的權值之和)。Dijkstra算法的思想是:對帶權有向圖G=(V,E),設置兩個頂點集合S和T=V-S;凡是以v0為源點並已確定了最短路徑的終點(頂點)都並入到集合S,集合S的初態只含有源點v0;而未確定其最短路徑的頂點均屬於集合T,初態時集合T包含除了源點v0之外的其他頂點。按照各頂點與v0間最短路徑的長度遞增的次序,逐個把集合T中的各頂點的路徑長度。並且,集合S中每加入一個新的頂點u,都要修改源點v0到集合T中剩余頂點的最短路徑長度;也即,集合T中各頂點v新的最短路徑長度值或是原來最短路徑長度值,或是頂點u的最短路徑長度值再加上頂點u到頂點v的路徑長度值之和這兩者中的較小值。這種把集合T中的頂點加入到集合S中的過程不斷重復,直到集合T的頂點全部加入到集合S中為止。
    Dijkstra算法的實現中,以二維數組gm作為n個頂點帶權有向圖G=(V,E)的存儲結構,並設置一個一維數組s(下標是0~n-1)用來標記集合S中已找到最短路徑的頂點,而且規定:如果s[i]為0,則表示未找到源點v0到頂點vi的最短路徑,也即此時vi在集合T中;如果s[i]為1,則表示已找到源點v0到頂點vi的最短路徑(此時vi在集合S中).除了數組s外,還設置了一個數組dist(下標是0~n-1),用來保存從源點v0到終點vi的當前最短路徑的長度.dist的初值為<v0,vi>邊上的權值;若v0到vi沒有邊,則權值為&(無窮)。此后每當有一個新的頂點進入集合S中時,dist[i]值可能被修改變小.一維數組path(下標是0~n-1)用於保存最短路徑長度中路徑上邊所經過的頂點序列;其中,path[i]保存從源點v0到終點vi當前最短路徑中前一個頂點編號,它的初值是:如果v0到vi有邊則置path[i]為v0的編號;如果v0到vi沒有邊則置path[i]為-1.
 參考代碼:
 
 1 #include<stdio.h>
 2  #define MAXSIZE 6
 3  #define INF  32767
 4 
 5  void Ppath(int path[],int i,int v0)//先序遞歸查找最短路徑(源點為v0)上的頂點
 6  {
 7    int k;
 8    k=path[i];
 9     if(k!=v0)//頂點Vk不是源點V0時
10    {
11      Ppath(path,k,v0);//遞歸查找頂點Vk的前一個頂點
12      printf("%d,",k);//輸出頂點Vk
13     }
14   }
15 
16  void Dispath(int dist[],int path[],int s[],int v0,int n)//輸出最短路徑
17  {
18    int i;
19    for(i=0;i<n;i++)
20    if(s[i]==1)//頂點Vi在集合S中
21    {
22     printf("從%d到%d的最短路徑長度為:%d,路徑為:",v0,i,dist[i]);
23     printf("%d,",v0);//輸出路徑上的源點v0;
24     Ppath(path,i,v0);//輸出路徑上的中間頂點vi
25     printf("%d\n",i);//輸出路徑上的終點
26      }
27    else
28   printf("從%d到%d不存在路徑\n",v0,i);
29 }
30 
31 void Dijkstra(int gm[][MAXSIZE],int v0,int n)//Dijkstra算法
32 {
33   int dist[MAXSIZE],path[MAXSIZE],s[MAXSIZE];
34   int i,j,k,mindis;
35   for(i=0;i<n;i++)
36   {
37    dist[i]=gm[v0][i];//v0到vi的最短路徑初值賦給dist[i]
38    s[i]=0;//s[i]=0表示頂點vi屬於T集
39    if(gm[v0][i]<INF)//路徑初始化,INF為可取的最大常數
40     path[i]=v0;
41    else
42     path[i]=-1;//v0到vi沒有邊
43 }
44 s[v0]=1;path[v0]=0;//V0並入集合S且V0當前最短路徑中無前一個頂點
45 for(i=0;i<n;i++)//對除V0外的n-1個頂點尋找最短路徑,即循環n-1次
46  {
47   mindis=INF;
48   for(j=0;j<n;j++)//從當前集合T中選擇一個路徑長度最短的頂點Vk
49   if(s[j]==0&&dist[j]<mindis)
50   {
51    k=j;
52   mindis=dist[j];
53   }
54  s[k]=1;//頂點Vk加入集合S中
55   for(j=0;j<n;j++)//調整源點v0到集合T中任一頂點Vj的路徑長度
56    if(s[j]==0)//頂點vj在集合T中
57     if(gm[k][j]<INF&&dist[k]+gm[k][j]<dist[j])//當V0到Vj的路徑長度小於V0到Vk和Vk到Vj的路徑長度時
58     {
59      dist[j]=dist[k]+gm[k][j];
60      path[j]=k;//Vk是當前最短路徑中Vj的前一個頂點
61      }
62   }
63  Dispath(dist,path,s,v0,n);//輸出最短路徑
64 }
65 
66 void main()
67 {
68  int g[MAXSIZE][MAXSIZE]={{INF,20,15,INF,INF,INF},{2,INF,INF,INF,10,30},{INF,4,INF,INF,INF,10},
69                                           {INF,INF,INF,INF,INF,INF},{INF,INF,INF,15,INF,INF},{INF,INF,INF,4,10,INF}};//定義鄰接矩陣g
70 Dijkstra(g,0,6);//求頂點0的最短路徑
71 }

輸出:


帶權有向圖及鄰接矩陣示意:


免責聲明!

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



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