Floyd-Warshall算法(弗洛伊德算法)

算法概述

Floyd-Warshall算法是由Robert Floyd和Stephen Warshall于1962年提出的一种用于计算图中所有顶点对之间最短路径的经典动态规划算法。该算法能够处理带权有向图或无向图,可以包含负权边但不能包含负权环。

算法原理

Floyd-Warshall算法基于动态规划思想,其核心是通过逐步考虑中间节点来优化路径估计:

  1. 动态规划状态:dp[k][i][j]dp[k][i][j]dp[k][i][j] 表示只使用前 kkk 个节点作为中间节点时,从 iii 到 jjj 的最短距离
  2. 状态转移:考虑第 kkk 个节点是否作为路径的中间节点
  3. 逐步优化:通过增加中间节点来不断优化路径长度

算法步骤

  1. 初始化:

    • 创建距离矩阵 distdistdist,其中 dist[i][j]dist[i][j]dist[i][j] 表示从 iii 到 jjj 的直接距离
    • 如果 i=ji = ji=j,则 dist[i][j]=0dist[i][j] = 0dist[i][j]=0
    • 如果 iii 和 jjj 之间有边,则 dist[i][j]=w(i,j)dist[i][j] = w(i, j)dist[i][j]=w(i,j)
    • 否则 dist[i][j]=∞dist[i][j] = \inftydist[i][j]=∞
  2. 动态规划迭代:

    • 对于每个中间节点 kkk 从 1 到 VVV
    • 对于每对顶点 (i,j)(i, j)(i,j)
    • 更新 dist[i][j]=min⁡(dist[i][j],dist[i][k]+dist[k][j])dist[i][j] = \min(dist[i][j], dist[i][k] + dist[k][j])dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j])
  3. 结果输出:

    • 最终的 distdistdist 矩阵包含所有顶点对之间的最短距离

数学表达

设 G=(V,E)G = (V, E)G=(V,E) 为一个带权图,其中:

  • VVV 是顶点集合,∣V∣=n|V| = n∣V∣=n
  • EEE 是边集合
  • w(i,j)w(i, j)w(i,j) 表示边 (i,j)(i, j)(i,j) 的权重

Floyd-Warshall算法维护的距离矩阵 DDD 满足:
D(k)[i][j]=min⁡{D(k−1)[i][j]D(k−1)[i][k]+D(k−1)[k][j]D^{(k)}[i][j] = \min \begin{cases} D^{(k-1)}[i][j] \\ D^{(k-1)}[i][k] + D^{(k-1)}[k][j] \end{cases}D(k)[i][j]=min{D(k−1)[i][j]D(k−1)[i][k]+D(k−1)[k][j]​

其中 D(k)[i][j]D^{(k)}[i][j]D(k)[i][j] 表示只使用前 kkk 个节点作为中间节点时,从 iii 到 jjj 的最短距离。

时间复杂度

  • 时间复杂度:O(V3)O(V^3)O(V3)
  • 空间复杂度:O(V2)O(V^2)O(V2)

其中:

  • VVV 是顶点数

算法特点

优点

  1. 能够计算所有顶点对之间的最短路径
  2. 算法实现简单,易于理解
  3. 可以处理含负权边的图(但不能含负权环)
  4. 能够检测负权环

缺点

  1. 时间复杂度较高,不适用于大规模图
  2. 空间复杂度较高,需要 O(V2)O(V^2)O(V2) 的存储空间
  3. 对于稀疏图效率较低

应用场景

  1. 全最短路径计算:需要计算图中所有顶点对之间的最短路径
  2. 网络分析:分析网络中任意两点间的最短距离
  3. 交通规划:城市间交通网络的最短路径分析
  4. 图像处理:某些图像处理算法中的距离计算

伪代码

function FloydWarshall(Graph):
    n ← number of vertices in Graph
    dist ← new n×n matrix
    
    // 初始化距离矩阵
    for i from 1 to n:
        for j from 1 to n:
            if i == j:
                dist[i][j] ← 0
            else if (i, j) ∈ Graph:
                dist[i][j] ← weight(i, j)
            else:
                dist[i][j] ← ∞
    
    // 动态规划迭代
    for k from 1 to n:
        for i from 1 to n:
            for j from 1 to n:
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] ← dist[i][k] + dist[k][j]
    
    // 检测负权环
    for i from 1 to n:
        if dist[i][i] < 0:
            error "Graph contains a negative-weight cycle"
    
    return dist

实现示例

Python实现

def floyd_warshall(graph):
    # 获取所有节点
    nodes = list(graph.keys())
    n = len(nodes)
    node_index = {node: i for i, node in enumerate(nodes)}
    
    # 初始化距离矩阵
    dist = [[float('infinity')] * n for _ in range(n)]
    
    # 设置对角线为0
    for i in range(n):
        dist[i][i] = 0
    
    # 填充初始距离
    for u in graph:
        for v, weight in graph[u].items():
            i = node_index[u]
            j = node_index[v]
            dist[i][j] = weight
    
    # 动态规划迭代
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    
    # 检测负权环
    for i in range(n):
        if dist[i][i] < 0:
            raise ValueError("图中存在负权环")
    
    # 转换为节点名称的字典
    result = {}
    for i, u in enumerate(nodes):
        result[u] = {}
        for j, v in enumerate(nodes):
            result[u][v] = dist[i][j]
    
    return result

算法分析

空间优化

Floyd-Warshall算法可以使用空间优化技术将空间复杂度从 O(V2)O(V^2)O(V2) 降低到 O(V)O(V)O(V):

def floyd_warshall_space_optimized(graph):
    nodes = list(graph.keys())
    n = len(nodes)
    node_index = {node: i for i, node in enumerate(nodes)}
    
    # 使用一维数组进行空间优化
    dist = [float('infinity')] * n
    
    for u in graph:
        for v, weight in graph[u].items():
            i = node_index[u]
            j = node_index[v]
            dist[i * n + j] = weight
    
    # 动态规划迭代
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if dist[i * n + k] + dist[k * n + j] < dist[i * n + j]:
                    dist[i * n + j] = dist[i * n + k] + dist[k * n + j]
    
    return dist

路径重构

除了计算最短距离,Floyd-Warshall算法还可以重构最短路径:

def floyd_warshall_with_path(graph):
    nodes = list(graph.keys())
    n = len(nodes)
    node_index = {node: i for i, node in enumerate(nodes)}
    
    # 初始化距离矩阵和前驱矩阵
    dist = [[float('infinity')] * n for _ in range(n)]
    next_node = [[-1] * n for _ in range(n)]
    
    for i in range(n):
        dist[i][i] = 0
    
    for u in graph:
        for v, weight in graph[u].items():
            i = node_index[u]
            j = node_index[v]
            dist[i][j] = weight
            next_node[i][j] = j
    
    # 动态规划迭代
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
                    next_node[i][j] = next_node[i][k]
    
    # 获取路径的函数
    def get_path(i, j):
        if next_node[i][j] == -1:
            return []
        path = [i]
        while i != j:
            i = next_node[i][j]
            path.append(i)
        return path
    
    return dist, get_path

算法比较

与Johnson算法的比较

特性Floyd-WarshallJohnson
时间复杂度O(V3)O(V^3)O(V3)O(V2log⁡V+V⋅E)O(V^2 \log V + V \cdot E)O(V2logV+V⋅E)
空间复杂度O(V2)O(V^2)O(V2)O(V2)O(V^2)O(V2)
适用场景密集图稀疏图
实现复杂度简单较复杂

与多次Dijkstra的比较

特性Floyd-Warshall多次Dijkstra
时间复杂度O(V3)O(V^3)O(V3)O(V⋅(V+E)log⁡V)O(V \cdot (V + E) \log V)O(V⋅(V+E)logV)
空间复杂度O(V2)O(V^2)O(V2)O(V+E)O(V + E)O(V+E)
适用场景密集图稀疏图
实现复杂度简单较复杂

注意事项

  1. 确保图中不包含负权环,否则算法会检测到并报错
  2. 对于大规模图,考虑使用Johnson算法或其他优化方法
  3. 在实现时注意处理无穷大的表示和比较
  4. 可以通过空间优化减少内存使用

总结

Floyd-Warshall算法作为计算所有顶点对最短路径的经典算法,具有重要的理论价值和实际应用意义。虽然其时间复杂度较高,但在需要计算全最短路径的场景中具有不可替代的作用。算法的动态规划思想和实现简单性使其成为图论教学和实际应用中的重要工具。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐