一個例子讓你明白一個演算法-Dijkstra(求源點到各頂點最短路徑)
阿新 • • 發佈:2018-12-29
演算法思想
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
*/
怎麼樣。。。。這個演算法掌握沒呢。。。。歡迎指錯!