一. 最短路径
在网图和非网图中,最短路径的含义是不同的
非网图:因为边上没有权值,所谓最短路径,其实是指两个顶点之间经过的边数最少的路径。
网图:两点之间经过的边上权值之和最少的路径。称路径上第一个顶点是源点,最后一个顶点是终点。
二. 迪杰斯特拉(Dijkstra)算法
用于单源最短路径
1. 算法思路
这是一个按照路径长度递增的次序产生最短路径的算法。
- 初始化:先找出从源点 V0 到各终点 Vk 的直达路径,即通过一条弧到达的路径。
- 选择:从这些路径中找出一条长度最短的路径(V0,U)。
- 更新:然后对其余各条路径进行适当调整。
若在图中存在弧(U,Vk),且(V0,U)+(U,Vk)<(V0,Vk),则以路径(V0,U,Vk)代替(V0,Vk)。 - 在调整后的各条路径中,再找长度最短的路径,一次类推
思路:
-
把 V 分为两组:
① S: 以求出最短路径的顶点的集合
② T=V-S:尚未确定最短路径的顶点集合 -
将 T 中顶点按最短路径递增的次序加入到 S 中
保证: ① 从源点 V0 到 S 中各顶点的最短路径长度都不大于从 V0 到 T 中任何顶点的最短路径长度
② 每个顶点对应一个距离值:
S 中的顶点:从 V0 到此顶点的最短路径长
T 中的顶点:从 V0 到此顶点的只包括 S 中顶点作中间顶点的最短路径长
图解:
初始:S = {V0},T = {其余顶点},T 中对应的距离值用辅助数组 D 存放
D[i] 初值:若<V0,Vi>存在,则为权值;否则为∞
从 T 中选择一个距离最小的顶点 Vj,加入 S
对 T 中顶点的距离进行修改,若加入 Vj 作为中间顶点,从 V0 到 Vi 的距离值比不加 Vj 是要短,则修改此距离
重复上述步骤,知道 S = V 为止
2. 代码实现
一维数组 final:记录从源点 v0 到终点 v 的最短路径是否已经确定
一维数组 path:记录从源点 v0 到终点 v 的当前最短路径上 v 的直接前驱顶点序号
一维数组D:记录从源点 v0 到终点 v 的当前最短路径
//求有向网 G 的 v0 顶点到其余顶点的最短路径
void ShortestPath_Dijkstra(MGraph G, int v0,int *D,int *path)
{
int i, v, w, k,min;
int final[MAXVEX]; //数组中值为 0 表示该顶点处于未知最短路径状态
for (i = 0; i < G.numv; i++)
{
final[i] = 0; //初始时都未知
D[i] = G.arc[v0][i]; //将 v0 到各个终点的最短路径长度初始化为弧的权值
//若 v0 与 v 之间有弧,将 v 的前驱置为0
//若 v0 与 v 之间没有弧,将 v 的前驱置为-1
if (D[i] < INFINITY)
{
path[i] = v0;
}
else
{
path[i] = -1;
}
}
final[v0] = 1; //v0 至 v0不需要求路径
D[v0] = 0; //v0 至 v0 路径为0
/*----------------------------------------------------------------------*/
//初始化结束
//对其余n-1各点依次计算
for (v = 1; v < G.numv; v++)
{
min = INFINITY;
for (w = 0; w < G.numv; w++) //寻找离 v0 最近的顶点
{
if (!final[w] && D[w] < min)
{
k = w;
min = D[w]; //w 顶点离 v0 最近
}
}
final[k] = 1;
for (w = 0; w < G.numv; w++) //修正当前最短路径及距离
{
if (!final[w] && min + G.arc[k][w] < D[w]) //如果经过该顶点的距离比现在的距离更短
{
D[w] = min + G.arc[k][w]; //修改当前路径长度
path[w] = k;
}
}
}
}