Floyd 算法

Floyd 算法

August 12, 2024

弗洛伊德算法

该算法与迪杰斯特拉算法不同,迪杰斯特拉是用于解决单源路径最短问题,而弗洛伊德算法是为了解决多源路径最短问题。

基本思想

先说明:在该问题上使用的思路是 动态规划

首先声明问题:我们要求的是从集合 S 中任意一点到其他点的最短路径。

此处就和迪杰斯特拉算法产生了不同,迪杰斯特拉算法是只要求一个点,而弗洛伊德算法是计算所有点。

故我们可以提出一个比较很容易想,但效率并不高的算法:

是否可以简单地对所有点都使用一遍迪杰斯特拉算法呢?

答案是可以的

并且只需要在标准的迪杰斯特拉算法外侧包裹上一层 for 循环遍历所有的元素即可

但这样效率并不好。

如果使用动态规划的方式来实现,思路如下:

从 \(i\) 到 \(j\) 节点可以看作两种情况,1 是二者直接相连,2 是二者中间经过若干个点相连。

所以,我们假设 \(Dis(i,j)\) 为节点 \(u\) 到节点 \(v\) 的最短路径的距离,对于每一个节点 \(k\) ,我们检查 \(Dis(i,k) + Dis(k,j) < Dis(i,j)\) 是否成立,如果成立,证明从 \(i\) 到 \(k\) 再到 \(j\) 的路径比 \(i\) 直接到 \(j\) 的路径短,我们便设置 \(Dis(i,j) = Dis(i,k) + Dis(k,j)\) ,这样一来,当我们遍历完所有节点 \(k\) , \(Dis(i,j)\) 中记录的便是 \(i\) 到 \(j\) 的最短路径的距离。

伪代码实现

输入:一个图的邻接矩阵 dist,其中 dist[i][j] 表示从顶点 i 到顶点 j 的边权,如果没有边则为无穷大(∞)
输出:更新后的邻接矩阵 dist,其中 dist[i][j] 表示从顶点 i 到顶点 j 的最短路径长度

1. 初始化:
   对于每一对顶点 (i, j),如果 i == j,则 dist[i][j] = 0
   如果 (i, j) 有边,则 dist[i][j] = 边的权重
   如果 (i, j) 没有边且 i != j,则 dist[i][j] = ∞

2. 对于每个中间顶点 k 从 1 到 n:
   3. 对于每个顶点 i 从 1 到 n:
      4. 对于每个顶点 j 从 1 到 n:
         5. 如果 dist[i][j] > dist[i][k] + dist[k][j]:
            6. 更新 dist[i][j] = dist[i][k] + dist[k][j]

7. 返回 dist

C 代码实现

#include <limits.h>

#define V 4 // 图中顶点的数量
#define INF INT_MAX // 无穷大

void floydWarshall(int dist[V][V]) {
    // 更新每对顶点之间的最短路径
    for (int k = 0; k < V; k++)
        for (int i = 0; i < V; i++)
            for (int j = 0; j < V; j++)
                if (dist[i][k] != INF && dist[k][j] != INF && dist[i][j] > dist[i][k] + dist[k][j])
                    dist[i][j] = dist[i][k] + dist[k][j]; // 更新最短路径
}

int main() {
    // 初始化图的邻接矩阵
    int dist[V][V] = {
        {0, 5, INF, 10},
        {INF, 0, 3, INF},
        {INF, INF, 0, 1},
        {INF, INF, INF, 0}
    };

    floydWarshall(dist);

    return 0;
}
最后更新于