Floyd-Warshall——仅用4行代码就能解决多源最短路径问题的算法

13 篇文章 0 订阅

前言

Floyd-Warshall算法简记Floyd算法,又称弗洛伊德算法,是解决任意两点间的最短路径问题的一种常用算法,核心思想就是“不断利用第三者影响原配关系”,这一点在4行核心代码中表现的淋漓尽致,接下来梳理一下算法思路。

Floyd思路

从A点走到B点要想路径最短只有两种可能,一种就是直接从A到B,另一种就是通过其他点来中转,Floyd的思路就是先把直接能到达的点固定下来,然后不断的尝试从其他点来中转来降低路程。

Floyd算法实现通常使用一个二维数组来表示任意两点之间的初始距离,每个点到自身的距离为0,若两个点之间没有直接连通,则赋值为 +∞,我们假设这个二维数组是 v,则 v[i][j] 代表了从点 i 到点 j 的初始距离。

假设不允许中转,那么二维数组 v 中的数据就代表了任意两点间的距离。

如果允许中转一次,我们假设只允许从节点1进行中转,那么点 i 到点 j 的最近距离最小为 v[i][j] 或者 v[i][1] + v[1][j],如果 v[i][1] + v[1][j] 的值更小,我们可以使用它来更新 v[i][j] 的值,这时 v[i][j] 就不仅仅是一个值了,而是隐含着 i->1->j 这样一条路径,这个过程实际上翻译成代码就是:

for(int i = 0; i < n; i++)
    for(int j = 0; j < n; j++)
        v[i][j] = min(v[i][j], v[i][1] + v[1][j]);

那么这条路径怎样才能更短呢?

答案就是引入另一个点,比如我们不仅允许从节点1中转,也允许从节点2中转,从上一步我们知道从从点 i 到点 j 的最短距离是从 i->1->j 得到的,实际上经过上面一步,任意两点的距离都是允许从节点1中转条件下的最小值, 那么引入节点2之后就是要看看 v[i][j]v[i][2] + v[2][j] 谁更小一点,然后遍历更新即可,类似的代码可以写成:

for(int i = 0; i < n; i++)
    for(int j = 0; j < n; j++)
        v[i][j] = min(v[i][j], v[i][2] + v[2][j]);

看到套路了没有,就是每个点都作为一个可能中转的点来试一下,整个算法就结束了,好神奇~ 完整4行代码如下:

for(int k = 0; k < n; k++)
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            v[i][j] = min(v[i][j], v[i][k] + v[k][j]);

简单粗暴又不失美感!

示例

初始路径及每条边的距离如图:

路径

  1. 翻译成二维数组如下:
0274
04
910
6150
  1. 仅通过节点0作为中转,二维数组更新如下:
0274
04
91013
68130
  1. 增加节点1作为中转,二维数组更新如下:
0264
04
91013
68120
  1. 再增加节点2作为中转,二维数组更新如下:
0264
130417
91013
68120
  1. 最后增加节点3作为中转,二维数组更新如下:
0264
130417
91013
68120

至此我们就求解出了任意两点间的最小距离。

总结

  • Floyd算法的时间复杂度为O(N^3),空间复杂度为 O(N^2)
  • Floyd是一种求解多源最短路径的算法,如果是求解单源最短路径这 N^3 的时间复杂度确实有点伤
  • Floyd可以正确处理有向图或存在负权边的图,但不能处理存在负权回路的图的最短路径问题
  • 4行代码3层循环或许可以助它称为最容易让人理解的最短路径算法
  • 这4行代码只是一个理想化的模型,实际在编码时要注意加法的越界问题,因为两个无穷大相加理论上是无穷大,但在代码里可能就崩溃了

==>> 反爬链接,请勿点击,原地爆炸,概不负责!<<==

人生到底是追求到达目的地还是准备欣赏沿途的风景,一味地向前奔跑忽略了周围的一切,很多美好的事物就在身边却不自知,我们已经被世俗蒙蔽了双眼,什么时候可以慢下来呢?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AlbertS

常来“玩”啊~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值