Floyd-Warshall 算法,中文亦称弗洛伊德算法或佛洛依德算法,是一种利用动态规划的思想解决多源(任意两点间)最短路径的算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。 Floyd-Warshall 算法的时间复杂度为 ,空间复杂度为 ,其中 是点集(来自维基百科的定义)。
- 「负权」指的是图中存在具有负权值的边,负权边表示从一个顶点到另一个顶点的距离是负值。Floyd-Warshall 算法可以正确处理包含负权边的图的最短路径问题。
- 「闭包」是指图中的传递关系。在有向图中,如果存在一条路径从顶点 A 到顶点 B,那么就说顶点 A 可以到达顶点 B。传递闭包就是将所有可以通过路径到达的顶点对进行标记,形成一个闭包集合。Floyd-Warshall 算法可以用来计算有向图的传递闭包,即找出所有顶点对之间是否存在路径。
- 「多源」指的是任意两点间的最短距离均可求出,「单源」指的是只能求一个顶点到其他点的最短距离,而不能求任意两点的最短距离。
Floyd 算法的基本思想是通过 中转节点来逐步优化路径长度 。该算法维护一个二维矩阵D,其中D[i][j] 表示节点 i 到节点 j 的最短路径长度。算法的核心是通过遍历所有节点 k,将节点k 作为中转节点,更新矩阵 D 的值。
Floyd 算法的具体步骤如下:
D,使得D[i][j] 表示节点 i 到节点 j 的直接距离,如果 i 和j之间没有直接边相连,则 D[i][j] 为无穷大,如果 i 和j相同,则 D[i][j] 为0,否则为直接相连的边的权值。k,遍历所有的节点i 和节点 j,如果D[i][k] + D[k][j] 的值小于 D[i][j],则更新D[i][j] 为D[i][k] + D[k][j]。k。D 即为所有节点对之间的最短路径长度。步骤二利用的是动态规划的思想,其状态转移方程为:D[i][j] = min(D[i][j], D[i][k] + D[k][j]);步骤一中则给出了如何初始化矩阵 D 和D[i][j]的定义。
Floyd 算法的时间复杂度为 ,空间复杂度为 ,其中 是点集。它的优点是可以处理带有负权边的图,并且可以同时求解任意两个节点之间的最短路径。然而,由于其时间复杂度较高,当节点数量较大时,可能会导致算法的运行时间较长。
D[i][j]表示节点 i 到节点 j 的直接距离。初始化时,如果 i 和j之间没有直接边相连,则 D[i][j] 为无穷大,如果 i 和j相同,则 D[i][j] 为0,否则为直接相连的边的权值。
例如,节点 A 和节点 B 直接相连,权值为 2,节点 B 和节点 C 没有直接相连,权值为无穷大。

对于每个节点 k,遍历所有的节点i 和节点 j,如果D[i][k] + D[k][j] 的值小于 D[i][j],则更新D[i][j] 为D[i][k] + D[k][j]。

例如,当遍历节点 A 时:
假设任意两点的间的距离,存储在邻接矩阵 graph 中,如果两点之间不直接相连,则该位置的值为无穷大 inf,否则为相应的距离值。
当然,也可以通过数组直接存储直接相连的边的信息,如
[[nodeA, nodeB, distAB], ..., [nodeC, nodeZ, distCZ]]。
1 |
|
该代码实现了 Floyd 算法,最终输出的结果是最短路径矩阵,显示了所有节点对之间的最短路径长度。
注意处理
inf + inf和x + inf相加会造成的数据溢出。
无向图 graph 中直接相连的节点:
1 | [0]---5---[1] |
运行结果:
1 | 最短路径矩阵为: |
对于无向图,节点对 (i, j) 和节点对 (j, i)具有相同的权值(距离)。当遍历每一个中转节点时,D[i][j]的值的更新,只跟三个位置有关,即 i, j, k;而此时 k 固定,在 i, j 的双重循环下,会产生重复操作,因此我们可以只遍历矩阵右上三角部分即可,即 j 从 i + 1 开始遍历。
1 | for (k = 0; k < n; k++) { |
Floyd 算法,俗称插点法,不就一个点 k 一个点 k' 地插进去,作为中转节点,更新节点对之间的距离嘛!