- Dijkstra
- Floyd
¶Dijkstra
讨论带权有向图
路径上的第一个顶点——源点(Source)
最后一个顶点——终点(Destination)
从某个源点到其余各顶点的最短路径
Dijkstra 算法
单源最短路径算法
引进一个辅助向量 $D$
它的每个分量 $D[\ i \ ]$ 表示当前所找到的从始点 $v$ 到每个终点 $v_{i}$ 的最短路径的长度
初态:若从 $v$ 到 $v_{i}$ 有弧,则 $D[\ i \ ]$ 为弧上的权值,否则置 $D[\ i \ ]$ 为 $\infty$
从 $v$ 出发的长度最短的一条最短路径 $(v,v_{j})$ :
$D[\ j \ ] = Min\{\ D[\ i \ ] \ \big| \ v_{i} \in V \}$按路径长度递增的次序来产生各最短路径
下一条长度次短的最短路径:
$D[\ j \ ] = Min\{\ D[\ i \ ] \ \big| \ v_{i} \in V - S\}$步骤表达式
(1)$D[\ i \ ] = arcs[\ Locate Vex(G, v) \ ]\ [\ i \ ]\ \ \ \ v_{i} \in V$
(2)$D[\ i \ ] = Min\{\ D[\ i \ ] \ \big| \ v_{i} \in V - S\}\ \ \ \ S = S \bigcup \{j \}$
(3)若$ D[\ i \ ] + arcs[\ j \ ] [\ k \ ] < D[\ k \ ]\ ,\ D[\ k \ ] = D[\ i \ ] + arcs[\ j \ ] [\ k \ ]$
(4)重复(2)(3)共 $n - 1$ 次
算法示例
始点 | 终点 | 最短路径 | 路径长度 |
---|---|---|---|
$v_0$ | $v_1$ | 无 | |
$v_2$ | $(v_0, v_2)$ | $10$ | |
$v_3$ | $(v_0, v_2, v_3)$ | $60$ | |
$v_4$ | $(v_0, v_4)$ | $30$ | |
$v_5$ | $(v_0, v_4, v_3, v_5)$ | $60$ |
邻接矩阵
$\begin{pmatrix} \infty & \infty & 10 & \infty & 30 & 100 \\ \infty & \infty & 5 & \infty & \infty & \infty \\ \infty & \infty & \infty & 50 & \infty & \infty \\ \infty & \infty & \infty & \infty & \infty & 10 \\ \infty & \infty & \infty & 20 & \infty & 60 \\ \infty & \infty & \infty & \infty & \infty & \infty \end{pmatrix}$(1)初始化$\ \ \ \ D[\ i \ ] = arcs[\ Locate Vex(G, v) \ ]\ [\ i \ ]\ \ \ \ v_{i} \in V$
$\ \ \ \ D[\ 0 \ ] = 0 \ \ \ \ S = \{\ 0 \ \}$ 第一个点,即始点
(2)$D[\ i \ ] = Min\{\ D[\ i \ ] \ \big| \ v_{i} \in V - S\}\ \ \ \ S = S \bigcup \{j \}$
找到与 $v_0$ 最近的点:$v_2\ \ \ \ min = 10\ \ \ \ D[\ 2 \ ] = 10\ \ \ \ S = \{\ 0,2 \ \}$
(3)若$ D[\ i \ ] + arcs[\ j \ ] [\ k \ ] < D[\ k \ ]\ ,\ D[\ k \ ] = D[\ i \ ] + arcs[\ j \ ] [\ k \ ]$
$\ \ \ \ 10(v_0 \rightarrow v_2) + 50(v_2 \rightarrow v_3) < \infty(v_0 \rightarrow v_3)$ ,满足, $D[\ 3 \ ] = 60$
后面皆不满足, $D[\ 4 \ ] = 30\ \ \ \ D[\ 5 \ ] = 100$
重复(2)(3)
(2’)$D[\ i \ ] = Min\{\ D[\ i \ ] \ \big| \ v_{i} \in V - S\}\ \ \ \ S = S \bigcup \{j \}$
找到与 $v_0$ 最近的点:$v_4\ \ \ \ min = 30\ \ \ \ D[\ 4 \ ] = 30\ \ \ \ S = \{\ 0,2,4 \ \}$
(3’)若$ D[\ i \ ] + arcs[\ j \ ] [\ k \ ] < D[\ k \ ]\ ,\ D[\ k \ ] = D[\ i \ ] + arcs[\ j \ ] [\ k \ ]$
$\ \ \ \ 30(v_0 \rightarrow v_4) + 20(v_4 \rightarrow v_3) < 60(v_0 \rightarrow v_3)$ ,满足, $D[\ 3 \ ] = 50$
$\ \ \ \ 30(v_0 \rightarrow v_4) + 60(v_4 \rightarrow v_5) < 100(v_0 \rightarrow v_3)$ ,满足, $D[\ 3 \ ] = 90$
(2’’)$D[\ i \ ] = Min\{\ D[\ i \ ] \ \big| \ v_{i} \in V - S\}\ \ \ \ S = S \bigcup \{j \}$
找到与 $v_0$ 最近的点:$v_3\ \ \ \ min = 50\ \ \ \ D[\ 3 \ ] = 50\ \ \ \ S = \{\ 0,2,3,4 \ \}$
(3’’)若$ D[\ i \ ] + arcs[\ j \ ] [\ k \ ] < D[\ k \ ]\ ,\ D[\ k \ ] = D[\ i \ ] + arcs[\ j \ ] [\ k \ ]$
$\ \ \ \ 50(v_0 \rightarrow v_3) + 10(v_3 \rightarrow v_5) < 90(v_0 \rightarrow v_3)$ ,满足, $D[\ 5 \ ] = 60$
(2’’’)$D[\ i \ ] = Min\{\ D[\ i \ ] \ \big| \ v_{i} \in V - S\}\ \ \ \ S = S \bigcup \{j \}$
找到与 $v_0$ 最近的点:$v_5\ \ \ \ min = 60\ \ \ \ D[\ 5 \ ] = 60\ \ \ \ S = \{\ 0,2,3,4,5 \ \}$
(3’’’)若$ D[\ i \ ] + arcs[\ j \ ] [\ k \ ] < D[\ k \ ]\ ,\ D[\ k \ ] = D[\ i \ ] + arcs[\ j \ ] [\ k \ ]$
(2’’’’)$D[\ i \ ] = Min\{\ D[\ i \ ] \ \big| \ v_{i} \in V - S\}\ \ \ \ S = S \bigcup \{j \}$
找到与 $v_0$ 最近的点:$v_1\ \ \ \ min = \infty\ \ \ \ D[\ 1 \ ] = \infty\ \ \ \ S = \{\ 0,1,2,3,4,5 \ \}$
(3’’’’)pass
(2)(3)共执行 $n-1 = 6-1 = 5$ 次,得到最短路径的带权长度
要想得到每条最短路径的具体路径,需持续更新 PathMatrix 二维数组
下面为 C 语言描述的 Dijkstra 算法
1 | void shortestPath_DIJ(MGraph G, int v0, PathMatrix &P, ShortPathTable &D) { |
PathMatrix P 的更新
邻接矩阵
$P[\ 6 \ ] [\ 6 \ ]$
初始化 P 全为 FALSE
$D[\ 0 \ ] = \infty\ \ \ \ D[\ 1 \ ] = \infty\ \ \ \ D[\ 2 \ ] = 10\ \ \ \ D[\ 3 \ ] = \infty\ \ \ \ D[\ 4 \ ] = 30\ \ \ \ D[\ 5 \ ] = 100$$if\ \ \ \ D[\ v \ ] < INFINITY \ \ \ \ P[\ v \ ] [\ v_0 \ ] = TRUE; \ \ \ \ P[\ v \ ] [\ v \ ] = TRUE;$
$\ \ \ \ P[\ 2 \ ] [\ 0 \ ] = TRUE; \ \ \ \ P[\ 2 \ ] [\ 2 \ ] = TRUE;$
$\ \ \ \ P[\ 4 \ ] [\ 0 \ ] = TRUE; \ \ \ \ P[\ 4 \ ] [\ 4 \ ] = TRUE;$
$\ \ \ \ P[\ 5 \ ] [\ 0 \ ] = TRUE; \ \ \ \ P[\ 5 \ ] [\ 5 \ ] = TRUE;$
$\begin{pmatrix}
& & & & & \\
& & & & & \\
T & & T & & & \\
& & & & & \\
T & & & & T & \\
T & & & & & T
\end{pmatrix}$
$P[w][w] = TRUE$
即 $P[\ w \ ] = P[\ v \ ] + P[\ w \ ]$
$D[\ 0 \ ] = 0\ \ \ \ D[\ 1 \ ] = \infty\ \ \ \ D[\ 2 \ ] = 10\ \ \ \ D[\ 3 \ ] = 60\ \ \ \ D[\ 4 \ ] = 30\ \ \ \ D[\ 5 \ ] = 100$
$\begin{pmatrix}
& & & & & \\
& & & & & \\
T & & T & & & \\
T & & T & T & & \\
T & & & & T & \\
T & & & & & T
\end{pmatrix}$
$30(v_0 \rightarrow v_4) + 60(v_4 \rightarrow v_5) < 100(v_0 \rightarrow v_3)$ ,满足, $D[\ 3 \ ] = 90$
$D[\ 0 \ ] = 0\ \ \ \ D[\ 1 \ ] = \infty\ \ \ \ D[\ 2 \ ] = 10\ \ \ \ D[\ 3 \ ] = 50\ \ \ \ D[\ 4 \ ] = 30\ \ \ \ D[\ 5 \ ] = 90$
$\begin{pmatrix}
& & & & & \\
& & & & & \\
T & & T & & & \\
T & & & T & T & \\
T & & & & T & \\
T & & & & T & T
\end{pmatrix}$
$D[\ 0 \ ] = 0\ \ \ \ D[\ 1 \ ] = \infty\ \ \ \ D[\ 2 \ ] = 10\ \ \ \ D[\ 3 \ ] = 50\ \ \ \ D[\ 4 \ ] = 30\ \ \ \ D[\ 5 \ ] = 60$
$\begin{pmatrix}
& & & & & \\
& & & & & \\
T & & T & & & \\
T & & & T & T & \\
T & & & & T & \\
T & & & T & T & T
\end{pmatrix}$
最终结果:
$D[\ 0 \ ] = 0\ \ \ \ D[\ 1 \ ] = \infty\ \ \ \ D[\ 2 \ ] = 10\ \ \ \ D[\ 3 \ ] = 50\ \ \ \ D[\ 4 \ ] = 30\ \ \ \ D[\ 5 \ ] = 60$改进 $P$
无须二维数组,可以记录前驱
只须建立一个前驱顶点数组
$prev[\ i \ ]$ 的值为顶点 $v_s$ 到 $v_i$ 的最短路径所经历的全部结点中的 $v_i$ 的前驱
初始化时,$prev[\ i \ ] = 0 \ \ \ \ (i = 0; i < G.vexnum; i++)$
更新 D 数组时,更新 prev 数组,$prev[\ w \ ] = v$
i | 0 | 1 | 2 | 3 | 4 | 5 | ||
---|---|---|---|---|---|---|---|---|
初始 | prev[i] | 0 | 0 | 0 | 0 | 0 | 0 | |
prev[i] | 2 | prev[3] = 2 | ||||||
prev[i] | 4 | 4 | prev[1] = 4; prev[5] = 4 | |||||
prev[i] | 4 | 3 | prev[5] = 3 | |||||
prev[i] | ||||||||
结束 | prev[i] | 0 | 0 | 0 | 4 | 0 | 3 |
前驱结点初始化???
初始化:
0 | 1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|---|
prev[i] | $\infty$ | $\infty$ | $\infty$ | $\infty$ | $\infty$ | $\infty$ |
邻接矩阵
$\begin{pmatrix}
\infty & \infty & 10 & \infty & 30 & 100 \\
\infty & \infty & 5 & \infty & \infty & \infty \\
\infty & \infty & \infty & 50 & \infty & \infty \\
\infty & \infty & \infty & \infty & \infty & 10 \\
\infty & \infty & \infty & 20 & \infty & 60 \\
\infty & \infty & \infty & \infty & \infty & \infty
\end{pmatrix}$
0 | 1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|---|
prev[i] | $\infty$ | $\infty$ | 0 | $\infty$ | 0 | 0 |
结果:
0 | 1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|---|
prev[i] | $\infty$ | $\infty$ | 0 | 4 | 0 | 3 |
C++ 伪代码
1 | for (int i = 0; i < G.vexnum; i++) { |
¶Floyd
Floyd 算法(有向无向 / 边权正负)
多源最短路径算法
$O(n^3)$
讨论有向带权图
对于稠密图,效率高于执行 $|v|$ 次 Dijkstra
引入 $map$ 矩阵
$map(i, j)$ 表示节点 $i$ 至 节点 $j$ 最短路径的距离
对于每个节点 $k$ ,检查 $map(i, k) + map(k, j) < map(i, j)$
若成立,则 $map(i, j) = map(i, k) + map(k, j)$
主要步骤:
(1)初始化 $map$ 矩阵
若不相邻,则 $map[\ i \ ] [\ j \ ] = \infty$
若是相邻(即有边),则 $map[\ i \ ] [\ j \ ] = G.arcs[\ i \ ] [\ j \ ]$
若 $i = j$ ,$map[\ i \ ] [\ j \ ] = 0$
(2)分别以每个顶点为中介点,更新 $map$ 矩阵
1 | for (k = 1; k <= n; k++) |