Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >弗洛伊德算法—–最短路径算法(一)

弗洛伊德算法—–最短路径算法(一)

作者头像
全栈程序员站长
发布于 2022-09-05 06:57:28
发布于 2022-09-05 06:57:28
76500
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

学习此算法的原因:昨天下午遛弯的时候,碰到闺蜜正在看算法,突然问我会不会弗洛伊德算法?我就顺道答应,然后用了半个小时的时间,学习了此算法,并用5分钟讲解给她听,在此也分享给各位需要的朋友,让你们在最短的时间内,透彻的掌握该算法。

Robert W. Floyd(罗伯特 弗洛伊德)1962年在“Communication of the ACM”上发表了该算法,同年Stephen Warshall(史蒂芬 沃舍尔)也独立发表该算法。弗洛伊德算法可以正确处理有向图或有向图或负权(但不可存在负权回路)的最短路径问题,同时也被用于计算有向图的传递闭包。

既然说是求最短路径的算法,那么首先我们先来看一个例子。

上图中有4个城市8条公路,公路上的数字表示这条公路的长短。请注意这些公路是单向的。我们需要求任意两个城市之间的最短路径,也就是求任意两个点之间的最短路径。这个问题也被称为“多源最短路径”问题。

现在我们用一个矩阵(4*4的二维数组e)对上图的信息进行存储。比如1号城市到2号城市的路程为2,则设e[1][2]的值为2。2号城市无法到达4号城市,则设置e[2][4]的值为∞。另外此处约定一个城市自己到自己的也是0,例如e[1][1]为0,具体如下。

现在回到问题:如何用本文算法求任意两点之间最短路径呢?

我们来想一想,根据我们以往的经验,如果要让任意两点(例如从顶点a点到顶点b)之间的路径变短,只能引入第三个点(顶点k),并通过这个顶点k中转即a->k->b,才可能缩短原来从顶点a点到顶点b的路程。那么这个中转的顶点k是1~n中的哪个点呢?甚至有时候不只通过一个点,而是经过两个点或者更多点中转会更短,即a->k1->k2->b或者a->k1->k2->…->k->i…->b。比如上图中从4号城市到3号城市(4->3)的路程e[4][3]原来是12。如果只通过1号城市中转(4->1->3),路程将缩短为11(e[4][1]+e[1][3]=5+6=11)。其实1号城市到3号城市也可以通过2号城市中转,使得1号到3号城市的路程缩短为5(e[1][2]+e[2][3]=2+3=5)。所以如果同时经过1号和2号两个城市中转的话,从4号城市到3号城市的路程会进一步缩短为10。通过这个的例子,我们发现每个顶点都有可能使得另外两个顶点之间的路程变短。好,下面我们将这个问题一般化。

当任意两点之间不允许经过第三个点时,这些城市之间最短路程就是初始路程,如下

如现在只允许经过1号顶点,求任意两点之间的最短路程,应该 如何求呢?只需判断e[i][1]+e[1][j]是否比e[i][j]要小即可。e[i][j]表示的是从i号顶点到j号顶点之间的路程。e[i][1]+e[1][j]表示的是从i号顶点到1号顶点,再从1号顶点到j号顶点的路程之和。其中i是1~n循环,j也是1~n循环,代码实现如下。

在只允许经过1号顶点的情况下,任意两点之间的最短路径更新为:

通过上图我们发现:在只通过1号顶点中转的情况下,3号顶点到2号顶点(e[3][2])、4号顶点到2号顶点(e[4][2])以及4号顶点到3号顶点(e[4][3])的路程都变短了。

接下来继续求在只允许经过1和2号两个顶点的情况下任意两点之间的最短路程。如何做呢?我们需要在只允许经过1号顶点时任意两点的最短路程的结果下,再判断如果经过2号顶点是否可以使得i号顶点到j号顶点之间的路程变得更短。即判断e[i][2]+e[2][j]是否比e[i][j]要小,代码实现为如下。

代码语言:javascript
代码运行次数:0
运行
复制
  1. //经过1号顶点
  2. for(i=1;i<=n;i++)
  3. for(j=1;j<=n;j++)
  4. if (e[i][j] > e[i][1]+e[1][j]) e[i][j]=e[i][1]+e[1][j];
  5. //经过2号顶点
  6. for(i=1;i<=n;i++)
  7. for(j=1;j<=n;j++)
  8. if (e[i][j] > e[i][2]+e[2][j]) e[i][j]=e[i][2]+e[2][j];

在只允许经过1和2号顶点的情况下,任意两点之间的最短路程更新为:

通过上图得知,在相比只允许通过1号顶点进行中转的情况下,这里允许通过1和2号顶点进行中转,使得e[1][3]和e[4][3]的路程变得更短了。

同理,继续在只允许经过1、2和3号顶点进行中转的情况下,求任意两点之间的最短路程。任意两点之间的最短路程更新为:

最后允许通过所有顶点作为中转,任意两点之间最终的最短路程为:

整个算法过程虽然说起来很麻烦,但是代码实现却非常简单,核心代码只有五行:

代码语言:javascript
代码运行次数:0
运行
复制
  1. for(k=1;k<=n;k++)
  2. for(i=1;i<=n;i++)
  3. for(j=1;j<=n;j++)
  4. if(e[i][j]>e[i][k]+e[k][j])
  5. e[i][j]=e[i][k]+e[k][j];

这段代码的基本思想就是:最开始只允许经过1号顶点进行中转,接下来只允许经过1和2号顶点进行中转……允许经过1~n号所有顶点进行中转,求任意两点之间的最短路程。用一句话概括就是:从i号顶点到j号顶点只经过前k号点的最短路程。其实这是一种“动态规划”的思想,关于这个思想我们将在《啊哈!算法2——伟大思维闪耀时》在做详细的讨论。下面给出这个算法的完整代码:

代码语言:javascript
代码运行次数:0
运行
复制
  1. #include <stdio.h>
  2. int main()
  3. {
  4. int e[10][10],k,i,j,n,m,t1,t2,t3;
  5. int inf=99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值
  6. //读入n和m,n表示顶点个数,m表示边的条数
  7. scanf(“%d %d”,&n,&m);
  8. //初始化
  9. for(i=1;i<=n;i++)
  10. for(j=1;j<=n;j++)
  11. if(i==j) e[i][j]=0;
  12. else e[i][j]=inf;
  13. //读入边
  14. for(i=1;i<=m;i++)
  15. {
  16. scanf(“%d %d %d”,&t1,&t2,&t3);
  17. e[t1][t2]=t3;
  18. }
  19. //Floyd-Warshall算法核心语句
  20. for(k=1;k<=n;k++)
  21. for(i=1;i<=n;i++)
  22. for(j=1;j<=n;j++)
  23. if(e[i][j]>e[i][k]+e[k][j] )
  24. e[i][j]=e[i][k]+e[k][j];
  25. //输出最终的结果
  26. for(i=1;i<=n;i++)
  27. {
  28. for(j=1;j<=n;j++)
  29. {
  30. printf(“%10d”,e[i][j]);
  31. }
  32. printf(“\n”);
  33. }
  34. return 0;
  35. }

有一点需要注意的是:如何表示正无穷。我们通常将正无穷定义为99999999,因为这样即使两个正无穷相加,其和仍然不超过int类型的范围(C语言int类型可以存储的最大正整数是2147483647)。在实际应用中最好估计一下最短路径的上限,只需要设置比它大一点既可以。例如有100条边,每条边不超过100的话,只需将正无穷设置为10001即可。如果你认为正无穷和其它值相加得到一个大于正无穷的数是不被允许的话,我们只需在比较的时候加两个判断条件就可以了,请注意下面代码中带有下划线的语句。

代码语言:javascript
代码运行次数:0
运行
复制
  1. //Floyd-Warshall算法核心语句
  2. for(k=1;k<=n;k++)
  3. for(i=1;i<=n;i++)
  4. for(j=1;j<=n;j++)
  5. if(e[i][k]<inf && e[k][j]<inf && e[i][j]>e[i][k]+e[k][j])
  6. e[i][j]=e[i][k]+e[k][j];

上面代码的输入数据样式为:

代码语言:javascript
代码运行次数:0
运行
复制
  1. 4 8
  2. 1 2 2
  3. 1 3 6
  4. 1 4 4
  5. 2 3 3
  6. 3 1 7
  7. 3 4 1
  8. 4 1 5
  9. 4 3 12

第一行两个数为n和m,n表示顶点个数,m表示边的条数。

接下来m行,每一行有三个数t1、t2 和t3,表示顶点t1到顶点t2的路程是t3。

得到最终结果如下:

通过这种方法我们可以求出任意两个点之间最短路径。它的时间复杂度是O(N3)。令人很震撼的是它竟然只有五行代码,实现起来非常容易。正是因为它实现起来非常容易,如果时间复杂度要求不高,使用Floyd-Warshall来求指定两点之间的最短路或者指定一个点到其余各个顶点的最短路径也是可行的。当然也有更快的算法,请看下一节:Dijkstra算法。

另外需要注意的是:Floyd-Warshall算法不能解决带有“负权回路”(或者叫“负权环”)的图,因为带有“负权回路”的图没有最短路。例如下面这个图就不存在1号顶点到3号顶点的最短路径。因为1->2->3->1->2->3->…->1->2->3这样路径中,每绕一次1->-2>3这样的环,最短路就会减少1,永远找不到最短路。其实如果一个图中带有“负权回路”那么这个图则没有最短路。

此算法由Robert W. Floyd(罗伯特·弗洛伊德)于1962年发表在“Communications of the ACM”上。同年Stephen Warshall(史蒂芬·沃舍尔)也独立发表了这个算法。Robert W.Floyd这个牛人是朵奇葩,他原本在芝加哥大学读的文学,但是因为当时美国经济不太景气,找工作比较困难,无奈之下到西屋电气公司当了一名计算机操作员,在IBM650机房值夜班,并由此开始了他的计算机生涯。此外他还和J.W.J. Williams(威廉姆斯)于1964年共同发明了著名的堆排序算法HEAPSORT。堆排序算法我们将在第七章学习。Robert W.Floyd在1987年获得了图灵奖。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/136154.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年6月4,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Floyd算法求解最短路径
  Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德。
别团等shy哥发育
2023/03/14
3.8K0
Floyd算法求解最短路径
【数据结构与算法】图最短路径算法 ( Floyed 算法 | 图最短路径算法使用场景 | 求解图中任意两个点之间的最短路径 | 邻接矩阵存储图数据 | 弗洛伊德算法总结 )
要令 A 到 B 之间的 距离 变短 , 只能 引入 第三个点 K , A 先到 K , 然后从 K 到 B ,
韩曙亮
2023/03/30
2.5K0
【数据结构与算法】图最短路径算法 ( Floyed 算法 | 图最短路径算法使用场景 | 求解图中任意两个点之间的最短路径 | 邻接矩阵存储图数据 | 弗洛伊德算法总结 )
只有五行的Floyd最短路径算法
暑假,小哼准备去一些城市旅游。有些城市之间有公路,有些城市之间则没有,如下图。为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程。
week
2022/11/26
3190
只有五行的Floyd最短路径算法
算法:最短路径之弗洛伊德(Floyd)算法
为了能讲明白弗洛伊德(Floyd)算法的主要思想,我们先来看最简单的案例。图7-7-12的左图是一个简单的3个顶点的连通网图。 我们先定义两个二维数组D[3][3]和P[3][3], D代表顶点与顶点
s1mba
2018/01/03
3.6K0
算法:最短路径之弗洛伊德(Floyd)算法
最短路径算法汇总「建议收藏」
在有向连通图中,从任意顶点i到顶点j的最短路径,可以看做从顶点i出发,经过m个顶点中转,到达j的最短路程。最开始可以只允许经过”1”号顶点进行中转,接下来只允许经过”1”号顶点和”2”号顶点进行中转……允许经过”1”~”m”号顶点进行中转,求任意两顶点的最短路程。
全栈程序员站长
2022/09/02
1K0
【数据结构与算法】图的最短路径算法实现:Dijkstra && Bellman-Ford && Floyd-Warshall
​ 最短路径问题:从在带权有向图 G 中的某一顶点出发,找出一条通往另一顶点的最短路径,最短也就是沿路径各边的权值总和达到最小。
利刃大大
2025/03/19
2780
【数据结构与算法】图的最短路径算法实现:Dijkstra && Bellman-Ford && Floyd-Warshall
Python 算法基础篇之最短路径算法: Dijkstra 算法和 Floyd-Warshall 算法
在计算机科学中,寻找图中最短路径是一个经典问题。 Dijkstra 算法和 Floyd-Warshall 算法是两种常用的最短路径算法。本篇博客将重点介绍这两种算法的原理、应用场景以及使用 Python 实现,并通过实例演示每一行代码的运行过程。
小蓝枣
2023/07/25
1.9K0
图的四种最短路径算法
本文总结了图的几种最短路径算法的实现:深度或广度优先搜索算法,弗洛伊德算法,迪杰斯特拉算法,Bellman-Ford算法
全栈程序员站长
2022/09/05
6020
弗洛伊德(Floyd's)算法—解决最短路径经典算法
弗洛伊德算法(Floyd's algorithm)是一种用于解决图中最短路径问题的经典算法。由美国计算机科学家罗伯特·弗洛伊德于1962年提出,该算法通过动态规划的思想,在图中寻找任意两个节点之间的最短路径,具有广泛的应用。本文将详细介绍弗洛伊德算法的原理、实现细节以及应用案例。
一条晒干的咸鱼
2024/11/19
8850
弗洛伊德(Floyd's)算法—解决最短路径经典算法
最短路径算法(下)——弗洛伊德(Floyd)算法
在这篇博客中我主要讲解最短路径算法中的Floyd算法,这是针对多源最短路径的一个经典算法。对于单源最短路径算法请详见我的另一篇博客:最短路径算法(上)——迪杰斯特拉(Dijikstra)算法
AI那点小事
2020/04/20
9030
最短路径算法(下)——弗洛伊德(Floyd)算法
全点对间最短路径(弗洛伊德算法)
之前学单源最短路径的时候,学到狄克斯特拉算法,我在想,如果对每个顶点都求它的单源最短路径,那不就可以得到全点对之间的最短路径了吗?这样算下来时间复杂度在O(|V|(|V|+|E|)log|V|)
灯珑LoGin
2022/10/31
4010
C++图论之常规最短路径算法的花式玩法(Floyd、Bellman、SPFA、Dijkstra算法合集)
权重图中的最短路径有两种,多源最短路径和单源最短路径。多源指任意点之间的最短路径。单源最短路径为求解从某一点出到到任意点之间的最短路径。多源、单源本质是相通的,可统称为图论的最短路径算法,最短路径算法较多:
一枚大果壳
2023/12/01
6520
C++图论之常规最短路径算法的花式玩法(Floyd、Bellman、SPFA、Dijkstra算法合集)
[图]最短路径-Floyd算法
<!--more--> > Floyd算法(Floyd-Warshall algorithm)又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。 -来自百度百科 前一篇文章:[第六章 图-Dijkstra算法](https://study.sqdxwz.com/index.php/archives/13/) 我们已经学习过了单源最短路径求解方法,这次我们来学习所有顶点间(任意两点间)的最短路径求解方法-Floyd算法。 对于求解任意两点最短路径的方式,我们也可以采用简单暴力将Dijkstra算法循环n遍(假设存在有n个顶点),也是可以求解任意两点间距离的,但是人类社会之所以会进步,难道仅仅是会使用筷子?还是好好学习更先进的算法-Floyd算法吧! **注:**采用此暴力的时间复杂度为:O(n^3)。
王荣胜
2020/03/12
3K0
图的五种最短路径算法
本文总结了图的几种最短路径算法的实现:深度或广度优先搜索算法,费罗伊德算法,迪杰斯特拉算法,Bellman-Ford 算法。
全栈程序员站长
2022/09/05
6830
软考高级架构师:图论应用-最短路径
图论是数学的一个分支,主要研究图的性质。在图论中,最短路径问题是一个经典问题,它旨在找到图中两个顶点之间的最短路径长度。这个问题在很多实际应用中都非常重要,比如在网络路由、社交网络分析、城市交通规划等领域。
明明如月学长
2024/05/25
1220
软考高级架构师:图论应用-最短路径
Floyd算法——求图中所有点之间最短路径
Floyd 算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。简单的来说,算法的主要思想是动态规划(dp),而求最短路径需要不断松弛。
为为为什么
2024/08/06
5380
Floyd算法——求图中所有点之间最短路径
关于最短路径算法的理解
“最短路径算法:Dijkstra算法,Bellman-Ford算法,Floyd算法和SPFA算法等。​从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径叫做最短路径。”
全栈程序员站长
2022/09/02
1.2K0
[数据结构拾遗]图的最短路径算法
在需要使用到相应算法时,能够帮助你回忆出常用的实现方案并且知晓其优缺点和适用环境。并不涉及十分具体的实现细节描述。
蛮三刀酱
2019/05/06
3.2K0
[数据结构拾遗]图的最短路径算法
python实现 最短路径算法
Floyd-Warshall算法是解决任意两点间的最短路径的一种算法。通常可以在任何图中使用,包括有向图、带负权边的图。
全栈程序员站长
2022/09/06
1.7K0
python实现 最短路径算法
弗洛伊德(Floyd)算法(C/C++)
弗洛伊德算法(Floyd's algorithm),又称为弗洛伊德-沃尔什算法(Floyd-Warshall algorithm),是一种用于在加权图中找到所有顶点对之间最短路径的算法。这个算法适用于有向图和无向图,并且可以处理负权重边,但不能处理负权重循环。
摆烂小白敲代码
2024/09/23
4290
弗洛伊德(Floyd)算法(C/C++)
推荐阅读
相关推荐
Floyd算法求解最短路径
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验