之前我们学习过弗洛伊德算法求最短路径,但是使用了三重循环,导致时间复杂度是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
#include
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];
}
结果如下: