Dijkstra算法不能计算权重含有负数的图,所以需要别的算法。Floyd-Warshall就是这种算法。这个算法需要一个前提,图中没有负数距离的环存在。
Floyd-Warshall算法的目的是输出所有点之间的最短距离。
算法用到了动态规划思想,最重要的是中间点概念。中间点就是一个路径去掉首位后剩余的点。另外,使用一个距离数组来保存距离。距离数组是个三维数组。
第一个维度是指路径用到了哪些点。d[0]是只用到了一个点的距离矩阵。
这个三维数组的每一项都是一个二维数组,也就是距离矩阵。
算法第一步是floyd-warshall分解
这是个求动态规范子问题的过程。
定义
D
i
j
(
k
)
D_{ij}^{(k)}
Dij(k)是i到j的最短路径,并且所有中间节点都在1~k的集合中。
动态规划递归的思路是
1 如果最短路径上没有k
那么最短路径就是
D
i
j
k
−
1
D_{ij}^{k-1}
Dijk−1。
2 如果最短路径上有k。
那么最短路径就是
D
i
k
(
k
−
1
)
+
D
k
j
(
k
−
1
)
D_{ik}^{(k-1)}+ D_{kj}^{(k-1)}
Dik(k−1)+Dkj(k−1)。
所以递归步骤就是:
D
i
j
(
k
)
=
m
i
n
{
D
i
j
(
k
−
1
)
,
D
i
k
(
k
−
1
)
+
D
k
j
(
k
−
1
)
}
D_{ij}^{(k)}=min\{D_{ij}^{(k-1)},D_{ik}^{(k-1)}+D_{kj}^{(k-1)}\}
Dij(k)=min{Dij(k−1),Dik(k−1)+Dkj(k−1)}
对于这个三维数组,就只能才用自底部向上的办法了。
算法步骤如下:
第一步 初始化k为0的矩阵
D
i
j
0
=
W
i
j
D_{ij}^0=W_{ij}
Dij0=Wij
因为递归是按k进行递归的
所以依次计算
D
1
D^1
D1,
D
2
D^2
D2,
D
3
D^3
D3,一直到
D
n
D^n
Dn矩阵。
需要注意的是
D
1
D^1
D1是相当于编程中的数组的0索引,而
D
0
D^0
D0是最早初始化的。所以D这个数组的长度其实是n+1。
用这个图测试
以S为例子
S到B的距离,最短路径是2,路径是SCAB这个路径.
S索引为3,c索引为2,a索引为0,b索引为1。
所以d[3][3][1]=2,代码如下:
def floyd_warshall(self):
d = [[[0 if i == j else float('inf') for j in self.__vertices] for i in self.__vertices]]
# 先初始化距离
for e in self.__edges:
d[0][e.from_index][e.to_index] = e.weight
n = len(self.__vertices)
for k in range(1, n + 1):
d.append([[float('inf') for _ in self.__vertices] for _ in self.__vertices])
for i in range(0, n):
for j in range(0, n):
d[k][i][j] = min(d[k - 1][i][j], d[k - 1][i][k - 1] + d[k - 1][k - 1][j])
return d[n]