貪心演算法 迪傑斯特拉演算法求最短路徑
之前我們學習過弗洛伊德演算法求最短路徑,但是使用了三重迴圈,導致時間複雜度是O(n^3),而迪傑斯特拉演算法應該是求最短路徑的最好的演算法了。
迪傑斯特拉演算法原理
迪傑斯特拉演算法實際上是使用貪心演算法和bfs來求最短問題的,它的核心思想是,按照頂點來迭代,每一次迭代挑選當前離源點最短的路徑(貪心思想),然後以挑選的這個最短路徑的頂點作為源點,再發起貪心選擇當前離源點最短的路徑。
它的核心實現使用了三個陣列:
d[] :用來儲存各頂點離初始頂點的最短路徑,例如d[2] = 10說明,頂點2離頂點0的最短路徑為10;
p[]:用來儲存各頂點離初始頂點最短路徑的中間點,例如p[3] = 2,說明頂點0離頂點3的最短路徑要經過頂點2;
use[]:記錄各節點是否已求得最短路徑,0表示未求得,1表示已求得
例項分析
(分析時,一定要牢牢記住這三個矩陣代表的含義)
首先初始化
d[] = {0,1,5,∞,∞,∞}
p[] = {0,0,0,0,0,0}
use[] = {1,0,0,0,0,0} use[0]為1,是因為v0-v0不存在,我們當它已求得最短路徑
第一輪迭代:
求得離v0最近的是v1,再以v1為源點,求各頂點離v1的最短路徑,最後加上v0-v1的最短路徑1,所得結果:
d[] = {0,1,4,8,6,∞}
p[] = {0,0,1,1,1,0}
use[] = {1,1,0,0,0,0}
第二輪迭代:
求得離v0最近的是v2(v1已求得,排除),再以v2為源點,求各頂點離v2的最短路徑,最後加上v0-v2的最短路徑4,所得結果:
d[] = {0,1,4,8,5,11}
p[] = {0,0,1,1,2,2}
use[] = {1,1,1,0,0,0}
第三輪迭代:
求得離v0最近的是v4(v1,v2已求得,排除),再以v4為源點,求各頂點離v4的最短路徑,最後加上v0-v4的最短路徑5,所得結果:
d[] = {0,1,4,7,5,8}
p[] = {0,0,1,4,2,4}
use[] = {1,1,1,0,1,0}
第四輪迭代…….
第五輪迭代…….
……..
程式碼實現
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 10000;
const int INF = 10000;
int d[MAXN];//記錄v0到各定點的最小路徑
int use[MAXN];//記錄各節點是否已求得最短路徑,0表示未求得,1表示已求得
int p[MAXN];//記錄v0到個頂點最小路徑的中間節點
int G[MAXN][MAXN];//圖的矩陣
int N;
int main(){
int i,j;
cin >> N;
//初始化圖
for(i = 0; i < N; i++){
for(j = 0; j < N; j++){
cin >> G[i][j];
}
}
fill(use, use+N, 0);
fill(p, p+N, 0);
for(i = 0; i < N; i++){
d[i] = G[0][i];
}
use[0] = 1;//由於v0-v0不存在,因此當v0已求得最短路徑
for(i = 1; i < N; i++){
int k, min = INF;//k為中間點,min為最小路徑
for(j = 0; j < N; j++){
if(!use[j] && d[j] < min){
min = d[j];
k = j;
}
}
use[k] = 1;
for(int w = 0; w < N; w++){
if(!use[w] && min + G[k][w] < d[w]){
d[w] = min + G[k][w];
p[w] = k;
}
}
}
//列印結果
int t = p[N-1];
cout << 5 << " << ";
while(t > 0){
cout << t << " << ";
t = p[t];
}
cout << 0;
cout << endl << d[N-1];
}
結果如下: