單源最短路徑問題[Dijkstra實現]
一、問題
帶權有向圖G(E,V), 找出從給定源頂點s到其它頂點v的權最小路徑。
“最短路徑” = 最小權
二、問題求解:
求1到5的最短路徑值?
三、執行過程:
如果大家對這個問題的要求還不是很明白的話那麼我再帶著大家走一遍:
第一次:從1-->2:10 此時從1-->3沒有路徑所有是無窮大 1-->4:30 1-->5:100那麼我們發現這一組組最小的是10也就是2這一點,所以我們再把2這一點加到集合裡面來,那麼2這一點就可以當作一個橋來用,
第二次:此時我們再從1à3就可以通過1-->2-->3:60其他的1-->4:30
1-->5:100 可以發現此時最小的應該是3,所以我們再把3這一點加入到這個集合裡面來,如此重複的去做這些事情,到最後可以發現1à5的最短路徑應該是60(1-->4-->3-->5)
四、Dijkstra虛擬碼:
int dijkstra(int s,int t) { 初始化S={空集} d[s] = 0; 其餘d值為正無窮大 while (NOT t in S) { 取出不在S中的最小的d[i]; for (所有不在S中且與i相鄰的點j) if (d[j] > d[i] + cost[i][j]) d[j] = d[i] + cost[i][j]; ( “鬆弛”操作” ) S = S + {i}; //把i點添加到集合S裡 } return d[t];}
為何鬆弛操作:
也就是說如果1-->3這點的值為dist[3]>dist[2]+map[2][3]
那麼dist[3]=dits[2]+map[2][3]
五、代碼實現:
#include <iostream>using namespace std;#define MAX 9999999#define LEN 210int map[LEN][LEN]; //某點到某點兩點間的的距離int dist[LEN]; //記錄當前點到源點的最短路徑長度int mark[LEN]; //加入進來的點的集合 //初始化map為正無窮大void init(){ int i,j; for(i=0;i<LEN;i++){ for(j=0;j<LEN;j++){ map[i][j]=MAX; } } }//n:多少條路 start:起始點 void myDijstra(int n,int start){ int i,j,min,k; for(i=1;i<=n;i++){ mark[i]=0;//沒有點加入 dist[i]=map[start][i];//初始 } mark[start]=1;//把起始點加進來 dist[start]=0; for(i=1;i<=n;i++){ min=MAX; for(j=1;j<=n;j++){ if(!mark[j] && dist[j]<min){ //取出不在mark裡的最小的dist[i] min=dist[j]; k=j;//標記 } } if(min==MAX) break; mark[k]=1;//把K加進來 //做鬆弛操作 for(j=1;j<=n;j++){ if(!mark[j] && dist[j]>dist[k]+map[k][j]){ dist[j]=dist[k]+map[k][j]; } } }} int main(){ int i,j,n,line; int a,b,d; cin>>n>>line; //輸入焦點和邊 init(); for(i=0;i<line;i++){ cin>>a>>b>>d; //輸入各邊的權值 if(map[a][b]>d){ map[a][b]=map[b][a]=d; } } myDijstra(n,1);//調用方法 //輸出1到5的最短路徑 cout<<dist[5]<<endl; return 0;}
六、測試資料:
5 7
1 2 10
1 4 30
1 5 100
2 3 50
3 5 10
4 3 20
4 5 60
結果: