图——单源最短路径(二)Dijkstra算法
Dijkstra算法解决的是带权重的有向图上单源最短路径问题,该算法要求图中任意一条边的权重必须为非负值。
1. Dijkstra算法基本思想
对于一个带权重的有向图G=(V,E),若要求其中的一个结点S到图中其余各结点的最短路径,Dijkstra算法的思想是:维护一组结点集合T,从源点S到集合T中的结点的最短路径都已经被找到。算法反复地从V-T中选择最短路径估计最小的结点U,将其加入集合T中,然后对从节点U出发的边进行松弛。
补充:
(1)最短路径估计:对于一个结点v,用一个属性v.d来记录从源点S到结点v的最短路径权重的上界,则v.d就是从源点S到v的最短路径估计。在Dijkstra算法中,通常会将源点的最短路径估计初始化为0,而将其他结点的最短路径估计初始化为一个极大值。
(2)松弛:对一条边(u, w)进行松弛,前提是从源点S到结点u的最短路径已经确定,而松弛就是看看是否能对从源点S到结点w的最短路径进行改善。具体来说,就是将从S到u之间的最短路径长度加上从结点u到结点w之间的距离,与当前的从S到w的最短路径估计进行比较,如果前者更小,则将后者更新为前者,且此时要将结点w在从S到w之间的最短路径上的前驱结点更改为u。该过程伪代码如下:
// 对边(u, w)进行松弛
// sw[k]表示从源点S到结点k的最短路径估计
// lastnode[k]表示结点k在从源点S到k之间的最短路径上的前驱结点
//matrix[i][j]表示图中从结点i到结点j的距离
void rexlax(int u, int w) {
if(sw[w] > sw[u] + matrix[u][w]) {
sw[w] = sw[u] + matrix[u][w];
lastnode[w] = u;
}
}
2. Dijkstra算法具体实现
2.1 数据结构
此处采用邻接矩阵来对带权(权重非负)有向图进行表示:
#define Maximum 1000
typedef int VexType;
typedef int MatrixType;
typedef struct GraphAdjMatrix {
VexType vertexlist[Maximum];
MatrixType matrix[Maximum][Maximum];
int vertexnumber;
int edgenumber;
};
2.2 代码实现
void dijkstra(GraphAdjMatrix g, int s, int *sw, int *lastnode) {
int i, j, k, n, m;
int *tip = (int*)malloc(sizeof(int)*(g.vertexnumber+2));
for(i=1; i<=g.vertexnumber; i++) {
sw[i] = g.matrix[s][i];
lastnode[i] = s;
tip[i] = 0;
}
sw[s] = 0;
for(i=1; i<=g.vertexnumber; i++) {
j = Biggest;
for(k=1; k<=g.vertexnumber; k++) {
if(tip[k]==0 && sw[k]<j) {
j = sw[k];
n = k;
}
}
tip[n] = 1;
for(k=1; k<=g.vertexnumber; k++) {
if(tip[k]==0 && sw[k] > sw[n] + g.matrix[n][k]) {
sw[k] = sw[n] + g.matrix[n][k];
lastnode[k] = n;
}
}
}
}
2.3 测试
测试的图为:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
#include<vector>
#include<sstream>
#include<list>
#include<stdlib.h>
#include<queue>
#include<algorithm>
using namespace std;
#define Maximum 1000
#define Biggest 100000000
typedef int VexType;
typedef int MatrixType;
typedef struct GraphAdjMatrix {
VexType vertexlist[Maximum];
MatrixType matrix[6][6] = { {0},
{0, 0, 10, Biggest, 5, Biggest},
{0, Biggest, 0, 1, 2, Biggest},
{0, Biggest, Biggest, 0, Biggest, 4},
{0, Biggest, 3, 9, 0, 2},
{0, 7, Biggest, 6, Biggest, 0} };
int vertexnumber;
int edgenumber;
};
void dijkstra(GraphAdjMatrix g, int s, int *sw, int *lastnode) {
int i, j, k, n, m;
int *tip = (int*)malloc(sizeof(int)*(g.vertexnumber+2));
for(i=1; i<=g.vertexnumber; i++) {
sw[i] = g.matrix[s][i];
lastnode[i] = s;
tip[i] = 0;
}
sw[s] = 0;
for(i=1; i<=g.vertexnumber; i++) {
j = Biggest;
for(k=1; k<=g.vertexnumber; k++) {
if(tip[k]==0 && sw[k]<j) {
j = sw[k];
n = k;
}
}
tip[n] = 1;
for(k=1; k<=g.vertexnumber; k++) {
if(tip[k]==0 && sw[k] > sw[n] + g.matrix[n][k]) {
sw[k] = sw[n] + g.matrix[n][k];
lastnode[k] = n;
}
}
}
}
int main() {
GraphAdjMatrix g;
g.edgenumber = 10;
g.vertexnumber = 5;
int *sw = (int*)malloc(sizeof(int)*(g.vertexnumber + 2));
int *lastnode = (int*)malloc(sizeof(int)*(g.vertexnumber+2));
int *temp = (int*)malloc(sizeof(int)*(g.vertexnumber+2)); //存储最短路径
int source = 1;
dijkstra(g, source, sw, lastnode);
int i, j, k, a;
for(i=1; i<=g.vertexnumber; i++) {
cout<<i<<":"<<sw[i]<<endl; //打印最短路径长度
//找出从源点到结点i的最短路径
j = 0;
temp[j++] = i;
k = lastnode[i];
while(k != source) {
temp[j++] = k;
k = lastnode[k];
}
temp[j++] = source;
for(a=j-2; a>=0; a--) {
cout<<"<"<<temp[a+1]<<","<<temp[a]<<"> "; //打印最短路径
}
cout<<endl<<endl;
}
return 0;
}
运行结果:
1:0
<1,1>
2:8
<1,4> <4,2>
3:9
<1,4> <4,2> <2,3>
4:5
<1,4>
5:7
<1,4> <4,5>