Dijkstra 算法
Dijkstra 算法适用于 所有边权为正 的图;它同时适用于有向图和无向图。
约定
¶单元最短路径算法用于计算源点到图中所有点的最短距离。为方便表述,以下说明中进行如下约定:
记图
的点集为 ,边集为记边权为
(若存在多条从 连向 的边,取权值最小的那条)记源点为
, 到 的 真实最短距离 为记
表示 到 的 当前已知最短距离,故 恒成立;
为方便描述,以下称在边
算法描述
¶初始化
,使得 ,准备一个空集合在
中选取点 满足 ,将 放入集合 中(松弛操作)用上一步获得的
更新从 出发的边 (实际上仅更新 中的边就可以了):令重复第 2 步,直到
算法结束后,有
算法原理
¶不难发现,
初始时,算法第 2 步取出的点必为
,故此时结论成立。不妨假设在算法运行中期时,结论仍成立,此时算法第 2 步取出的点为
,由算法第 3 步中的松弛操作可知,当前 是在点 尝试了以 中的任意(所有)点 作为父跳转点(即最短路中使用了边 )后计算出的值若此时
,则 必然只能选取 中的点作为父跳转点才能得到最短路径,又因为 ,即如果这种情况真的成立,则必有 使得 ,因为 dijkstra 处理的是正边权的图,故有 ,且 的父跳转点必不在 中,否则此时 (因为父跳转点在 中意味着已求出从 到 父跳转点最短距离,则此时也就求出了 ),这与 矛盾。类似地可证明此时
的父跳转点的父跳转点也不在 中,递归下去即整条从 到达 的路径上都不存在 中的点,但这是不可能的,因为 就在 中,故这样的 并不存在。因此此时
,即 .
由上述关于

优化
¶不难发现,上述算法中总共迭代
如果采取邻接表来存储边,则最多累计进行
程序实现
¶为方便表述,约定如下变量含义:
N
表示图中点的个数,即N
,并假定图中所有点按N
进行标号INF
表示一个超过最长路径长度的超大值s
表示源点w[x][y]
表示边 的边权,即w[x][y]
d[x]
对应上文中提到的 数列,即d[x]
visited[x]
表示点 是否在集合 中,即visited[x]
朴素 dijkstra 算法 (C++)
dijkstra.pure.cpp | 27 lines.123456789101112131415161718192021222324252627const int INF = 0x3f3f3f3f;void dijkstra(int N, int s, int** w, int* d, bool* visited) {// Initialize the `d` array.for (int x = 0; x < N; ++x) {d[x] = INF;visited[x] = false;}d[s] = 0;for (int u = 0; u < N; ++u) {int x, m = INF;for (int z = 0; z < N; ++z) {if (visited[z]) continue;if (d[z] <= m) m = d[x = z];}if (m == INF) break;// Perform the relaxation operation.visited[x] = true;for (int y = 0; y < N; ++y) {int v = d[x] + w[x][y];if (d[y] > v) d[y] = v;}}}优先队列优化的 Dijkstra 算法 (Typescript)
dijkstra.priority-queue.ts | 43 lines.12345678910111213141516171819202122232425262728293031323334353637383940414243import { PriorityQueue } from '@algorithm.ts/queue'export interface DijkstraEdge<T extends number | bigint> {/*** The other end of the edge.*/to: number/*** The cost of walking along this side.*/cost: T}export function dijkstra<T extends number | bigint>(N: number,source: number,G: Array<Array<DijkstraEdge<T>>>,ZERO: T,INF: T,): T[] {const dist: T[] = new Array(N).fill(INF)const Q = new PriorityQueue<{ pos: number; cost: T }>({compare: (x, y) => x.cost - y.cost,})// eslint-disable-next-line no-param-reassigndist[source] = ZEROQ.enqueue({ pos: source, cost: ZERO })while (Q.size > 0) {const { pos, cost } = Q.dequeue()!if (dist[pos] < cost) continuefor (const e of G[pos]) {const candidate: T = (dist[pos] as any) + e.costif (dist[e.to] > candidate) {// eslint-disable-next-line no-param-reassigndist[e.to] = candidateQ.enqueue({ pos: e.to, cost: dist[e.to] })}}}return dist}
Related
¶