算法描述:
Dijkstra解决的是带权重的有向图上单源最短路径问题,该算法要求所有边的权重都为非负值。
算法在运行过程中维持的关键信息是一组结点集合S,从源点s到该集合中每个结点之间的最短路径都已经被找到。
算法重复从结点集V-S中选择最短路径估计最小的结点u,将u加入到集合S,然后对所有从u出发的边进行松弛。
算法实现:
dist数组记录源点到图中结点的最短距离,初始均为无穷大,置dist[start] = 0供算法启动。
在结点u加入集合S中时,这条路径中u的前驱一定已经在集合S内了。
int edge[N][N]; int dist[N]; // 最短距离 int bset[N]; // 标记是否已在集合S int pre[N]; // 记录前驱 const int INF = 999999999; void Dijkstra(start) { fill(dist, dist + N, INF); fill(bset, bset + N, 0); dist[start] = 0; for(int i = 0; i < N; i++) { int min = INF; int u = -1; for(int j = 0; j < N; j++) { if(bset[j] == 0 && dist[j] < min) { u = j; min = dist[j]; } } if(u == -1) continue; bset[u] = 1; for(int j = 0; j < N; j++) { if(bset[j] == 0) {
// 松弛操作 if(dist[u] + edge[u][j] < dist[j]) { dist[j] = dist[u] + edge[u][j]; pre[j] = u; } else if(dist[u] + edge[u][j] == dist[j]) { // 说明到达该节点有多条最短路径 } } } } }
算法正确性证明:
在带权重的有向图G=(V,E)中,初始时S为空集。
我们需要证明的是:对于每次加入集合S的结点u来说,s到u的距离为最短距离。
记u.d为为s到u的距离,δ(s,u)为s到u的最短距离,即证明:
对于每次加入集合S的结点u来说,u.d = δ(s,u)
这里使用反证法证明:
假设:结点u是第一个加入到集合S时使得u.d ≠ δ(s,u)的结点
源结点s是第一个加入到集合S中的结点,并且s.d = δ(s,s) = 0, 结点u必定与结点s不同,即u ≠ s。
根据算法可知,当u加入到S时,一定存在某条从结点s到u的路径,并且u的前驱结点在集合S内,该路径记为P1。
由假设可知,还存在一条从s到u的最短路径,这条路径中u的前驱一定不在集合S中(不然松弛过程就会发现该路径),该路径记为P2。
当前要把u放入S内,b为集合S外结点。
由假设知:x.d = δ(s,x)
a.d = δ(s,x)
因为P2 < P1, 可推出dist[b] < dist[u],所以b会先于u进入,产生矛盾。