floyd-warshall算法

0214jx / 2024-11-22 / 原文

Floyd-warshall算法

问题描述

图的最短路径问题,多源最短路径问题求解

算法思路

  • 设Dijk为从i到j的只以(1...k)集合为中间节点的最短路径的长度,Dijk = min(Dijk-1, Dikk-1 + Dkjk-1)

    若最短路径经过点k,则Dijk = Dikk-1 + Dkjk-1;

    若最短路径不经过点k,则Dijk = Dijk-1

python代码如下:

graph = {'A':[(2, 'A', 'B')],
         'B':[(1, 'B', 'C'), (6, 'B', 'D')],
         'C':[(5, 'C', 'A'), (4, 'C', 'D')],
         'D':[(3, 'D', 'A')], 
        }
def graph2adjacent_matrix(graph):
    vertices = list(graph.keys())
    vnum = len(vertices)
    adjacent_matrix = [[0 if row == col else float('inf') for col in range(vnum)] for row in range(vnum)]
    for i in range(vnum):
        vertex = vertices[i]
        for edge in graph[vertex]:
            w, _, end_vertex = edge
            j = vertices.index(end_vertex)
            adjacent_matrix[i][j] = w
    return adjacent_matrix


def floyd(adjacent_matrix):
    vnum = len(adjacent_matrix)
    a = [[adjacent_matrix[row][col] for col in range(vnum)] for row in range(vnum)]
    nvertex = [[-1 if adjacent_matrix[row][col] == float('inf') else row for col in range(vnum)] for row in range(vnum)]
    for k in range(vnum):
        for i in range(vnum):
            for j in range(vnum):
                if a[i][j] > a[i][k] + a[k][j]:
                    a[i][j] = a[i][k] + a[k][j]
                    nvertex[i][j] = nvertex[k][j]
    return nvertex, a

adjacent_matrix = graph2adjacent_matrix(graph)
nvertex, a = floyd(adjacent_matrix)

for i in range(len(adjacent_matrix)):
    for j in range(len(adjacent_matrix[0])):
        print(adjacent_matrix[i][j], end='\t')
    print()
print('------------------------------------------------------------------------')
for i in range(len(nvertex)):
    for j in range (len(nvertex[0])):
        print(nvertex[i][j], end = '\t')
    print()
print('------------------------------------------------------------------------')
for i in range(len(a)):
    for j in range(len(a[0])):
        print(a[i][j], end='\t')
    print()
    

算法改进

  • 先比较再相加

    若Dikk-1 或Dkjk-1 > Dijk,则最短路径不经过点k,则Dijk = Dijk-1

    若Dikk-1 < Dijk且 Dkjk-1 < Dijk,则最短路径可能经过点k,则Dijk = min(Dijk-1, Dikk-1 + Dkjk-1)

  • 不难发现,对于每个点k,Dikk 以及Dkjk是不会更新的,故可以添加约束条件:

    if Dikk = 0,i++

    if Dkjk = 0,j++

  • 若点i与点k之间没有路径,那么点i到点j的最短路径一定不经过点k,故可添加约束条件:

    if Dikk-1 == inf, i++

    if Dkjk-1 == inf, j++

python代码如下:

def floyd(adjacent_matrix):
    vnum = len(adjacent_matrix)
    a = [[adjacent_matrix[row][col] for col in range(vnum)] for row in range(vnum)]
    nvertex = [[-1 if adjacent_matrix[row][col] == float('inf') else row for col in range(vnum)] for row in range(vnum)]
    for k in range(vnum):
        for i in range(vnum):
            if (a[i][k] == 'inf')| (i == k):
                continue
            for j in range(vnum):
                if (a[k][j] == 'inf')| (k == j):
                    continue
                elif (a[i][j] > a[i][k]) & (a[i][j] > a[k][j]):
                    if a[i][j] > a[i][k] + a[k][j]:
                        a[i][j] = a[i][k] + a[k][j]
                        nvertex[i][j] = nvertex[k][j]
    return nvertex, a