1.问题分析
给定有向带权图G = (V, E),其中每条边的权是非负实数。此外,给定V中的一个顶点u,称为源点。现在要计算从源到所有其它各顶点的最短路径,这里的路径指的是路径上各边的权值之和。
求单源最短路径的算法是Dijkstra,荷兰人,计算机科学家,在1972年获得图灵奖。
2.算法设计
Dijkstra是解决单源最短路径的贪心算法。算法的基本思想首先假定源点为u,顶点集合V被划分为两部分:集合S和集合V-S。初始时S中仅含有源点u,源点u到集合V-S中包含的顶点的最短路径待定。我们把从源点出发只经过S中的点到达V-S中的点的路径称为特殊路径,并用数组dist[]记录当前源点到每个点所对应的特殊路径的长度。
Diskstra算法采用的贪心策略是选择特殊路径长度最短的路径,将其连接的V-S中的顶点加入到集合S中,同时更新数组dist[]。一旦S中包含了所欲顶点,dist[]就是从源点到其它顶点之间的最短路径长度。
算法需要维护的数据结构有三个( 理解数据结构的意义是重点!):
邻接矩阵graph[][]:用来保存相邻顶点边上的权值,如果两个顶点之间没有边相连,则它们之间的权值为无穷大;
最短距离数组dist[i]:用来记录点i的最短路径长度;
前驱数组pre[i]:用来记录最短路径上顶点i的前驱;
选中顶点数组isIn[i]:bool类型,用来表示顶点i当前是否被选中。
算法执行流程
初始化。令集合S = {u},初始化graph[][],对于集合V-S中的所有顶点i,初始化dist[i] = map[u][i], 如果源点到顶点i之间有边相连,初始化pre[i] = u,否则pre[i] = -1。同时令isIn[u] = true,表示选中,其它顶点都设为false;(1)
找最小。在集合V-S中找出离源点最近的,就是在dist[]中找到值最小的顶点m,但前提是isIn[m]=false;(2)
将选中的顶点j加入到集合S中,同时更新V-S,这一步操作实际就是令isIn[m] = true;(3)
更新路径。选中顶点j后,需要判断和j相连的其它顶点k通过走顶点j是否可以缩短到源点到距离,即是否有“捷径”可走。如果dist[k] > dist[m] + graphm,说明有“捷径”可走,则更新dist[k] = dist[m] + graphm,同时更新顶点k的前驱,零pre[k] = m;(4)
判断S-V集合中是否为空,如果为空,算法结束,否则转到流程(3)继续选择;(5)
由此,可求得从源点u到图G的其余各个顶点的最短路径及长度,并且可以通过数组pre[]回溯找到最短路径上依次经过的顶点。
算法图解
(1) 原始数据结构
(2) 初始化最短距离数组dist[][]和前驱数组pre[]
(3) 找dist[]值最小的顶点并加入集合S
(4) 更新路径
(5) 继续找dist[]值最小的顶点并加入集合S
(6) 更新路径
(7) 继续找dist[]值最小的顶点并加入集合S
(8) 更新路径(此次没有数据需要更新!)