1. 程式人生 > >貪心演算法 迪傑斯特拉演算法求最短路徑

貪心演算法 迪傑斯特拉演算法求最短路徑

之前我們學習過弗洛伊德演算法求最短路徑,但是使用了三重迴圈,導致時間複雜度是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]; }

結果如下:

這裡寫圖片描述