问题描述
给定带权有向图G =(V,E),其中每条边的权是非负实数。另外,还给定V中的一个顶点,称为源。现在要计算从源到所有其他各顶点的最短路长度。这里路的长度是指路上各边权之和。这个问题通常称为单源最短路径问题。
如图:
解题思路:
设初始仅含有源顶点(起点)的集合s,设u是G的某一个顶点,把从源到u且中间只经过s中顶点的路称为从源到u的特殊路径,并用数组dist记录从源到每个顶点所对应的最短特殊路径,Dijkstra算法每次从V-s中取出具有最短特殊路径长度的顶点u,将u添加到S中,同时对dist数组做必要的修改。一旦s包含了所有V中的顶点,dist就记录了从源到所有其他顶点之间的最短路径长度。
所以基本流程是:判断最短从源到u的特殊路径-----》将u添加到集合s-----》更新dist[]值-----》重复以上流程直到s中包含所有顶点
使用Dijkstra算法的迭代过程
代码实现:
#include<iostream>
#include<iomanip>
#include<memory.h>
using namespace std;
#define N 100
#define maxint 99999
void Dijkstra(int n, int v, int dist[], int prev[],int c[][N]) {
//n代表顶点个数
//v为源点,即起点
//prev[i]记录从源到顶点i的最短路径i的前一个顶点,无路可走时为0
//dist[i]表示当前从源到顶点i的最短特殊路径长度
//c[][]为邻接矩阵
int i, j;
bool s[N];
//初始化dist[]和prev[]
for (i = 1; i <= n; i++) {
dist[i] = c[v][i];//顶点v直接到顶点i的距离
s[i] = false;
if (dist[i] == maxint) {
prev[i] = 0;
}
else {
prev[i] = v;
}
}
dist[v] = 0;
s[v] = true;//v为源点
//做n-1次贪心选择
for (i = 1; i < n; i++) {
int temp = maxint;
int u = v;//u 是用记录刚刚最近添加到 已有集合s[]中的顶点
//取出V-s中具有最短特殊路径长度的顶点u
for (j = 1; j <= n; j++) {//使用打擂台法,在剩下的路径长度中,选择当前状态下的最小值
if ((!s[j]) && dist[j] < temp) {
u = j;
temp = dist[j];
}
}
s[u] = true;//将当前具有最短特殊路径长度的顶点u 添加到集合s[]中
//根据做出的贪心选择更新dist值
for (j = 1; j <= n; j++) {
if ((!s[j]) && c[u][j] < maxint) {//当前加入的顶点u与j之间存在边
int newdist = dist[u] + c[u][j];//计算源点v到u加上u点到j点的总长度
if (newdist < dist[j]) {//如果源点v经过刚添加的u点,到达j点更短的话,则需要更新
dist[j] = newdist;
prev[j] = u;
}
}
}
}
}
//输出最短路径
void Traceback(int start, int end, int prev[]) {
if (start == end) {
cout << end;
return;
}
Traceback(start, prev[end], prev);
cout << "->" << end;
}
int main() {
int v = 1, n = 5, i, j;
int dist[N], prev[N];
int c[N][N] = {
{0},
{0,maxint,10,maxint,30,100},
{0,maxint,maxint,50,maxint,maxint},
{0,maxint,maxint,maxint,maxint,10},
{0,maxint,maxint,20,maxint,60},
{0,maxint,maxint,maxint,maxint,maxint}
};
cout << "每两个顶点之间的权值,即原始数据,从顶点1,1开始" << endl;
cout << "有向图权的矩阵为:" << endl;
for (i = 1; i <= n; i++) {
for (j = 1; j <= n; j++) {
if (c[i][j] == maxint) {
cout << setw(5) << "无";
}else {
cout << setw(5) << c[i][j];
}
}
cout << endl;
}
cout << "选择源点为: 1" << endl;
Dijkstra(n, v, dist, prev, c);
for (i = 1; i <= n; i++) {
cout << "源点1" << "到点" << i << "的最短路径长度为:" << dist[i] << ",其路径为";
Traceback(v, i, prev);
cout << endl;
}
return 0;
}