可以简单的通过运行|V|次单源最短路径算法来解决,每次使用一个不同的结点作为源结点
多数算法采用邻接矩阵来表示图,因此
算法的输入为一个n*n的矩阵W,代表一个有n个结点的有向图G = (V, E)的边的权重 wij = 0 若i = j 权重 若i != j,且(i, j)属于E INF 若i != j,且(i, j)不属于E
输出也是一个n *n的矩阵D = (dij),dij表示从结点i到结点j的一条最短路径的权重
以及一个前驱结点矩阵P = (pij),pij在i = j或者从i到j不存在路径时为NIL,在其他情况下给出从结点i到结点j的某条最短路径上结点j的前驱结点 矩阵的一行第i行所诱导的子图,应当是一棵根节点为i的最短路径树,最短路径的输出 PRINT-ALL-PAIRS-SHORTEST-PATH(P) for i = 1 to n for j = i+1 to n PRINT-PAIRS-SHORTEST-PAHT(P, i, j)
PRINT-PAIRS-SHORTEST-PAHT(P, i, j)
if(i == j)
print i
//递归输出的终止条件
if(pij == NIL)
print “no path from “i” to “j” exists
else
PRINT-PAIRS-SHORTEST-PAHT(P, i, pij)
print j
Floyd-Warshall算法(动态规划)
最短路径的结构
一条路径p = <v1, v2, …, vn>上的中间结点指除了v1和vn之外的任意结点,也就是处于集合{v2, …, vn}中的结点
图G所有结点V = {1, 2, …, n},考虑其中一个子集{1, 2, …, k};对任意结点对i, j属于V,考虑结点i到结点j的所有中间结点均取自集合{1, 2, …, k}的路径,并且设p为其中权重最小的路径
dij(k)表示该最短路径的权重
1) k不是路径p上的中间结点,则路径p上的所有中间结点都属于集合{1, 2, …, k-1};从而dij(k) = dij(k-1)
2) k是路径p上的中间结点,则路径p可以分解为i——>k——>j【p1和p2】,由于k不是p1或者p2上的中间结点,因为k是端点,从而dij(k) = dik(k-1) + dkj(k-1)
所有结点对最短路径问题的一个递归解 dij(k) = wij 若k = 0 min(dij(k-1), dik(k-1) + dkj(k-1) 若k >= 1
自底向上计算最短路径问题 FLOYD-WARSHALL(W) n = W.rows D(0) = W for k = 1 to n let D(k) be a new n * n matrix for i = 1 to n for j = 1 to n dij(k) = min(dij(k-1), dik(k-1)+dkj(k-1)) return D(n) 时间复杂度为O(n^3)
构建一条最短路径 在计算D的同时计算前驱矩阵P 初始化: pij(0) = NIL 若i = j 或 wij = INF i 若i != j 且 wij < INF 迭代更新: pij(k) = pij(k-1) 若
dij(k-1) <= dik(k-1)+dkj(k-1)
pkj(k-1) 若dij(k-1) > dik(k-1)+dkj(k-1)
然后再根据一开始的输出算法进行输出即可
Johnson算法(适用于稀疏图,输入为邻接链表)
如果图G = (V, E)中所有的边权重w皆为非负值,可以对每个结点运行一次Dijkstra算法来找到所有结点对之间的最短路径
如果图G = (V, E)中存在权重w为负的边,需要通过一种技术——重新赋予权重来将权重转换为正的,再采用Dijkstra算法
新赋予的权重必须满足两个提交
1) 不改变最短路径
2) 所有的边(u, v),新权重为非负值
具体做法,引入新结点s,对图G中每个结点v,定义h(v) = t(s, v),即s到v的最短路径长度
w'(u, v) = w(u, v) + h(u) – h(v)即可