1. 程式人生 > >一個例子讓你明白一個演算法-Dijkstra(求源點到各頂點最短路徑)

一個例子讓你明白一個演算法-Dijkstra(求源點到各頂點最短路徑)

演算法思想

1.在一個圖中,把所有頂點分為兩個集合P,Q(P為最短路徑集合,Q為待選集合),用dis陣列儲存源點到各個頂點的最短路徑(到自身為0)。
2.初始化P集合,就是加入源點到該集合,並在mark陣列標記(程式碼中的mark[y]=1),那麼Q集合就是剩下的頂點構成了。
3.在Q集合中找到這樣一個頂點:源點到該頂點(記為u)的路徑最短,把該點加入P集合,列出以u為起點的所有邊(終點記為v),判斷從源點到每一個v頂點(因為以u為起點的邊有多個)經過u頂點的路徑是否會變小,更新dis[v]的值。
4.重複3的步驟,直到Q集合為空,演算法結束,次時,dis陣列就儲存了從源點到各個頂點的最短路徑。

文字看的頭大了,還是來個例子吧

現在有一個圖是這樣的:
圖的例項
我們可以用二維陣列儲存它,可以看做這樣:
圖的二維陣列形式
程式碼實現是這樣的:

#include<stdio.h>
#include<string.h>
//巨集 
#define N 10 
#define INF 999999
//定義全域性變數
int e[N][N];//儲存圖二維陣列 
int dis[N];//dis[i]表示 源點到 i 的距離 
int mark[10];//標誌陣列,mark[i]==0表示沒有訪問過,相反的就不說了
int n,m;//圖的頂點數,和邊數


/*
*求源點(頂點 y )到各個頂點的最小路徑值,時刻記住這個演算法的作用哦
*/
void Dijkstra(int y){ int i,j,k;//迴圈控制變數 int min; int u; /*初始化相關資料*/ //memset(mark,0,sizeof(mark)); /*得到dis的初始值*/ for(i=1;i<=n;i++){ dis[i]=e[y][i]; } mark[y]=1;//記錄源點已經在集合P /*這個迴圈意味深長啊,體會下^_^*/ for(k=1;k<=n-1;k++){ /*從源點出發,到某點值最小,記錄該點資訊(值,位置)*/
min = INF;//初始化最小值 for(i=1;i<=n;i++){ /*若該點在集合Q中,並且值更小*/ if(mark[i]==0 && min>dis[i]){ /*記錄改點資訊*/ min = dis[i]; u = i; } } mark[u]=1;//記錄該點在集合P /*擴充套件該點*/ for(i=1;i<=n;i++){ /*若有從該點的出邊,該邊的起點為 u 終點為 i */ if(e[u][i] < INF){ /*若藉助 u 頂點,使 從源點到 頂點 i 的距離減少,則跟新 dis[i] 的值*/ if(dis[i] > dis[u]+e[u][i]){ dis[i] = dis[u]+e[u][i]; } } } } //輸出從源點到各點的最小路徑 for(i=1;i<=n;i++){ printf("%d ",dis[i]); } } int main(){ int a,b,c,y; /*讀入頂點數,邊數*/ scanf("%d%d",&n,&m); /*初始化圖,每一個頂點到自身的距離為0,其他先初始化為不可到達狀態*/ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(i==j){ e[i][j]=0; }else{ e[i][j]=INF; } } } //讀入每一條邊的資訊,起點,終點,權值 for(int i=1;i<=m;i++){ scanf("%d%d%d",&a,&b,&c); e[a][b]=c; } /*讀入源點*/ scanf("%d",&y); Dijkstra(y); } /* ****測試資料 6 9 1 2 1 1 3 12 2 3 9 2 4 3 3 5 5 4 3 4 4 5 13 4 6 15 5 6 4 1 ****測試結果 0 1 8 4 13 17 */

怎麼樣。。。。這個演算法掌握沒呢。。。。歡迎指錯!