弗洛伊德算法(求最短路径)

在一个加权图中,如果想找到各个顶点之间的最短路径,可以考虑使用弗洛伊德算法。

弗洛伊德算法既适用于无向加权图,也适用于有向加权图。使用弗洛伊德算法查找最短路径时,只允许环路的权值为负数,其它路径的权值必须为非负数,否则算法执行过程会出错。

弗洛伊德算法的实现思路

弗洛伊德算法是基于动态规划算法实现的,接下来我们以在图 1 所示的有向加权图中查找各个顶点之间的最短路径为例,讲解弗洛伊德算法的实现思路。

图 1 有向加权图

图 1 中不存在环路,且所有路径(边)的权值都为正数,因此可以使用弗洛伊德算法。

弗洛伊德算法查找图 1 中各个顶点之间的最短路径,实现过程如下:

  1. 建立一张表格,记录每个顶点直达其它所有顶点的权值:

表 1 各个顶点直达路径的权值

目标顶点

1 2 3 4
起始顶点 1 0 3 ∞ 5
2 2 0 ∞ 4
3 ∞ 1 0 ∞
4 ∞ ∞ 2 0

起始顶点指的是从哪个顶点出发,目标顶点指的是要达到的顶点,例如 2->1 路径的权值是 2,顶点 2 是起始顶点,顶点 1 是目标顶点。此外,∞ 表示无穷大的数,即顶点之间不存在直达的路径。

  1. 在表 1 的基础上,将顶点 1 作为 “中间顶点”,计算从各个顶点出发途径顶点 1 再到达其它顶点的权值,如果比表 1 中记录的权值更小,证明两个顶点之间存在更短的路径,对表 1 进行更新。

从各个顶点出发,途径顶点 1 再到达其它顶点的路径以及对应的权值分别是:

2-1-3:权值为 2 + ∞ = ∞,表 1 中记录的 2-3 的权值也是 ∞;
2-1-4:权值为 2 + 5 = 7,表 1 中记录的 2-4 的权值是 4;
3-1-2:权值为 ∞ + 3,表 1 中记录的 3-2 的权值是 1;
3-1-4:权值为 ∞ + 5,表 1 中记录的 3-4 的权值是 ∞;
4-1-2:权值为 ∞ + 3,表 1 中记录的 4-2 的权值是 ∞;
4-1-3:权值为 ∞ + ∞,表 1 中记录的 4-3 的权值是 2。

以上所有的路径中,没有比表 1 中记录的权值最小的路径,所以不需要对表 1 进行更新。

  1. 在表 1 的基础上,以顶点 2 作为 “中间顶点”,计算从各个顶点出发途径顶点 2 再到达其它顶点的权值:

1-2-3:权值为 3 + ∞,表 1 中记录的 1-3 的权值为 ∞;
1-2-4:权值为 3 + 4 = 7,表 1 中 1-4 的权值为 5;
3-2-1:权值为 1 + 2 = 3,表 1 中 3-1 的权值为 ∞,3 < ∞;
3-2-4:权值为 1 + 4 = 5,表 1 中 3-4 的权值为 ∞,5 < ∞;
4-2-1:权值为 ∞ + 2,表 1 中 4-1 的权值为 ∞;
4-2-3:权值为 ∞ + ∞,表 1 中 4-3 的权值为 2。

以顶点 2 作为 “中间顶点”,我们找到了比 3-1、3-4 更短的路径,对表 1 进行更新:

表 2 更新后的 “表 1”

目标顶点

1 2 3 4
起始顶点 1 0 3 ∞ 5
2 2 0 ∞ 4
3 3(3-2-1) 1 0 5(3-2-4)
4 ∞ ∞ 2 0

  1. 在表 2 的基础上,将顶点 3 作为 “中间顶点”,计算从各个顶点出发途径顶点 3 再到达其它顶点的权值:

1-3-2 权值为 ∞ + 1,表 2 中 1-2 的权值为 3;
1-3-4 权值为 ∞ + 5,表 2 中 1-4 的权值为 5;
2-3-1 权值为 ∞ + 3,表 2 中 2-1 的权值为 2;
2-3-4 权值为 ∞ + 5,表 2 中 2-4 的权值为 4;
4-3-1 权值为 2 + 3 = 5,表 2 中 4-1 的权值为 ∞,5 < ∞;
4-3-2 权值为 2 + 1 = 3,表 2 中 4-2 的权值为 ∞,3 < ∞;

以顶点 3 作为 “中间顶点”,我们找到了比 4-1、4-2 更短的路径,对表 2 进行更新:

表 3 更新后的 “表 2”

目标顶点

1 2 3 4
起始顶点 1 0 3 ∞ 5
2 2 0 ∞ 4
3 3(3-2-1) 1 0 5(3-2-4)
4 5(4-3-2-1) 3(4-3-2) 2 0

  1. 在表 3 的基础上,将顶点 4 作为 “中间顶点”,计算从各个顶点出发途径顶点 4 再到达其它顶点的权值:

1-4-2 权值为 5 + 3 = 8,表 3 中 1-2 的权值为 3;
1-4-3 权值为 5 + 2 = 7,表 3 中 1-3 的权值为 ∞,7 < ∞;
2-4-1 权值为 4 + 5 = 9,表 3 中 2-1 的权值为 2;
2-4-3 权值为 4 + 2 = 6,表 3 中 2-3 的权值为 ∞,6 < ∞;
3-4-1 权值为 4 + 5 = 9,表 3 中 3-1 的权值为 3;
3-4-2 权值为 5 + 5 = 10 ,表 3 中 3-2 的权值为 1。

以顶点 4 作为 “中间顶点”,我们找到了比 1-3、2-3 更短的路径,对表 3 进行更新:

表 4 更新后的 “表 3”

目标顶点

1 2 3 4
起始顶点 1 0 3 7(1-4-3) 5
2 2 0 6(2-4-3) 4
3 3(3-2-1) 1 0 5(3-2-4)
4 5(4-3-2-1) 3(4-3-2) 2 0
通过将所有的顶点分别作为“中间顶点”,最终得到的表 4 就记录了各个顶点之间的最短路径。例如,4-1 的最短路径为 4-3-2-1。

弗洛伊德算法的具体实现

了解了弗洛伊德算法查找最短路径的实现思路后,接下来仍以图 1 为例,分别编写 C、Java、Python 程序实现弗洛伊德算法。

如下是用弗洛伊德算法查找图 1 中各顶点之间最短路径的 C 语言程序:

#include <stdio.h>
#define V 4    //设定图中的顶点数
#define INF 65535   // 设置一个最大值
int P[V][V] = { 0 }; //记录各个顶点之间的最短路径
void printMatrix(int matrix[][V]);  //输出各个顶点之间的最短路径
void printPath(int i, int j); // 递归输出各个顶点之间最短路径的具体线路
void floydWarshall(int graph[][V]); // 实现弗洛伊德算法
int main() {// 有向加权图中各个顶点之间的路径信息int graph[V][V] = { {0, 3, INF, 5},{2, 0, INF, 4},{INF, 1, 0, INF},{INF, INF, 2, 0} };floydWarshall(graph);
}
// 中序递归输出各个顶点之间最短路径的具体线路
void printPath(int i, int j)
{int k = P[i][j];if (k == 0)return;printPath(i, k);printf("%d-", k + 1);printPath(k, j);
}
// 输出各个顶点之间的最短路径
void printMatrix(int graph[][V]) {int i, j;for (i = 0; i < V; i++) {for (j = 0; j < V; j++) {if (j == i) {continue;}printf("%d - %d: 最短路径为:", i + 1, j + 1);if (graph[i][j] == INF)printf("%s\n", "INF");else {printf("%d", graph[i][j]);printf(",依次经过:%d-", i + 1);//调用递归函数printPath(i, j);printf("%d\n", j + 1);}}}
}
// 实现弗洛伊德算法,graph[][V] 为有向加权图
void floydWarshall(int graph[][V]) {int  i, j, k;//遍历每个顶点,将其作为其它顶点之间的中间顶点,更新 graph 数组for (k = 0; k < V; k++) {for (i = 0; i < V; i++) {for (j = 0; j < V; j++) {//如果新的路径比之前记录的更短,则更新 graph 数组if (graph[i][k] + graph[k][j] < graph[i][j]) {graph[i][j] = graph[i][k] + graph[k][j];//记录此路径P[i][j] = k;}}}}// 输出各个顶点之间的最短路径printMatrix(graph);
}

如下是用弗洛伊德算法查找图 1 中各顶点之间最短路径的 Java 程序:

public class Floyd {static int V = 4; // 顶点的个数static int[][] P = new int[V][V]; // 记录各个顶点之间的最短路径static int INF = 65535; // 设置一个最大值// 中序递归输出各个顶点之间最短路径的具体线路public static void printPath(int i, int j) {int k = P[i][j];if (k == 0)return;printPath(i, k);System.out.print((k + 1) + "-");printPath(k, j);}// 输出各个顶点之间的最短路径public static void printMatrix(int[][] graph) {for (int i = 0; i < V; i++) {for (int j = 0; j < V; j++) {if (j == i) {continue;}System.out.print((i + 1) + " - " + (j + 1) + ":最短路径为:");if (graph[i][j] == INF)System.out.println("INF");else {System.out.print(graph[i][j]);System.out.print(",依次经过:" + (i + 1) + "-");// 调用递归函数printPath(i, j);System.out.println((j + 1));}}}}// 实现弗洛伊德算法,graph[][V] 为有向加权图public static void floydWarshall(int[][] graph) {int i, j, k;// 遍历每个顶点,将其作为其它顶点之间的中间顶点,更新 graph 数组for (k = 0; k < V; k++) {for (i = 0; i < V; i++) {for (j = 0; j < V; j++) {// 如果新的路径比之前记录的更短,则更新 graph 数组if (graph[i][k] + graph[k][j] < graph[i][j]) {graph[i][j] = graph[i][k] + graph[k][j];// 记录此路径P[i][j] = k;}}}}// 输出各个顶点之间的最短路径printMatrix(graph);}public static void main(String[] args) {// 有向加权图中各个顶点之间的路径信息int[][] graph = new int[][] { { 0, 3, INF, 5 }, { 2, 0, INF, 4 }, { INF, 1, 0, INF }, { INF, INF, 2, 0 } };floydWarshall(graph);}
}

如下是用弗洛伊德算法查找图 1 中各顶点之间最短路径的 Python 程序:

V = 4   # 顶点的个数
INF = 65535    # 设定一个最大值
P = [[0]*V for i in range(V)] # 记录各个顶点之间的最短路径
# 有向加权图中各个顶点之间的路径信息
graph = [[0, 3, INF, 5],[2, 0, INF, 4],[INF, 1, 0, INF],[INF, INF, 2, 0]]
# 中序递归输出各个顶点之间最短路径的具体线路
def printPath(i,j):k = P[i][j]if k == 0:return;printPath(i , k)print("%d-" % (k + 1) , end='')printPath(k , j)
# 输出各个顶点之间的最短路径
def printMatrix(graph):for i in range(V):for j in range(V):if j == i:continueprint("%d - %d: 最短路径为:"%(i + 1, j + 1) , end='')if graph[i][j] == INF:print("INF")else:print(graph[i][j] , end='')print(",依次经过:%d-"%(i+1) , end='')# 调用递归函数printPath(i , j)print(j + 1)
# 实现弗洛伊德算法,graph[][V] 为有向加权图
def floydWarshall(graph):# 遍历每个顶点,将其作为其它顶点之间的中间顶点,更新 graph 数组for k in range(V):for i in range(V):for j in range(V):# 如果新的路径比之前记录的更短,则更新 graph 数组if graph[i][k] + graph[k][j] < graph[i][j]:graph[i][j] = graph[i][k] + graph[k][j]# 记录此路径P[i][j] = k# 输出各个顶点之间的最短路径printMatrix(graph)
floydWarshall(graph)

以上程序的输出结果均为:
1 - 2: 最短路径为:3,依次经过:1-2
1 - 3: 最短路径为:7,依次经过:1-4-3
1 - 4: 最短路径为:5,依次经过:1-4
2 - 1: 最短路径为:2,依次经过:2-1
2 - 3: 最短路径为:6,依次经过:2-4-3
2 - 4: 最短路径为:4,依次经过:2-4
3 - 1: 最短路径为:3,依次经过:3-2-1
3 - 2: 最短路径为:1,依次经过:3-2
3 - 4: 最短路径为:5,依次经过:3-2-4
4 - 1: 最短路径为:5,依次经过:4-3-2-1
4 - 2: 最短路径为:3,依次经过:4-3-2
4 - 3: 最短路径为:2,依次经过:4-3

弗洛伊德算法(求最短路径)相关推荐

  1. .弗洛伊德算法求最短路径

    5. 弗洛伊德算法求最短路径 [问题描述] 对于下面一张若干个城市,以及城市之间距离的地图,请采用弗洛伊德算法求出所有城市之间的最短路径. [输入形式] 顶点个数n,以及n*n的邻接矩阵,其中不可达使 ...

  2. 校园导航系统之用弗洛伊德算法求加权图的最短路径

    其实这个示例是在大一数据结构课程设计的时候选的题目,只不过在昨天的算法分析与设计实验课上又看到了求加权图的最短路径,忽然想起当初课程设计时为了弄懂Floyd算法而不断抓狂的过程,觉得有必要将它从邮箱的 ...

  3. C++迪杰斯特拉算法求最短路径

    一:算法历史 迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法.是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题.迪杰斯特拉算法主要特点是以 ...

  4. C++floyd warshall算法求最短路径(附完整源码)

    C++floyd warshall算法求最短路径 floyd warshall算法求最短路径的完整源码(定义,实现,main函数测试) floyd warshall算法求最短路径的完整源码(定义,实现 ...

  5. 有n 个长为m+1 的字符串,求前后m个字符匹配所能形成的最长字符串链:利用弗洛伊德算法求最长路径...

    有n 个长为m+1 的字符串,如果某个字符串的最后m 个字符与某个字符串的前m 个字符匹配,则两个字符串可以联接,问这n 个字符串最多可以连成一个多长的字符串,如果出现循环,则返回错误. 把字符串看成 ...

  6. 佛洛依德算法求最短路径实例

    佛洛依德算法求最短路径实例 #include <iostream> #include <string.h> #include <stdlib.h> #include ...

  7. 动态规划 - Floyd算法求最短路径 - (Matlab建模)

    Floyd算法又称为弗洛伊德算法.插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似.该算法名称以创始人之一.1978年图灵奖获得者.斯坦福大学计算 ...

  8. 数据结构:弗洛伊德算法(最短路径)图文详解

    弗洛伊德算法选取某个节点k作为i到j需要经过的中间节点,通过比较d(i,k)+d(k,j)和现有d(i,j)的大小,将较小值更新为路径长度,对k节点的选取进行遍历,以得到在经过所有节点时i到j的最短路 ...

  9. _DataStructure_C_Impl:Dijkstra算法求最短路径

    // _DataStructure_C_Impl:Dijkstra #include<stdio.h> #include<stdlib.h> #include<strin ...

  10. Java图结构-模拟校园地图-迪杰斯特拉(Dijkstra)算法求最短路径 #谭子

    目录目录 一.前言 二.模拟校园地图描述 三.分析题目及相关绘图 四.代码部分 1.GraphNode类 2.Menu类(管理文字) 3.Attraction类 4.AttractionGraph类( ...

最新文章

  1. vue图片不存在时加载默认图片
  2. 小程序不同页面之间通讯的解决方案 1
  3. Linux服务器中查询IP地址五个方法
  4. 关于chrome上的网银安全控件开发技术(chrome 调用本地dll)
  5. C语言实现HDB3编码与译码
  6. php车牌输入,微信小程序车牌号码模拟键盘输入功能的实现代码
  7. 关于奈奎斯特图的一些解读
  8. 各个排序算法(^_^)
  9. 使用Requests爬取网页图片并保存
  10. 电脑城最简单骗局,仍然有无数人上当
  11. 中英文说明书丨艾美捷HEK293T宿主细胞蛋白ELISA试剂盒
  12. 亚马逊新手卖家如何快速出单必掌握的运营技巧分享?
  13. 用C语言构造康托集,洛谷——P1014 Cantor表
  14. 第七章租赁法律与合同
  15. 超微主板升级bios_没法用新CPU给老主板更新BIOS?别着急,AMD借你一块CPU
  16. 游戏开发公司如何开发一款小游戏
  17. 使用putty等ssh工具第一次远程登录树莓派,填默认账号密码pi,raspberry,报错:Access denied
  18. Task03 session和cookie、代理、selenium自动化 拔高:丁香园留言板爬取
  19. 快速获取瑞星杀毒软件一年免费版
  20. 深入谈谈String.intern()在JVM的实现

热门文章

  1. linux桌面版本安装MSDM,_如何安装linux操作系统?
  2. 学习WEB前端开发是选择自学还是去培训机构?
  3. Maven学习:项目构建生命周期
  4. 对抗样本(论文解读八):Towards More Robust Adversarial Attack Against Real World Object Detectors
  5. 个人对生活意义的观点
  6. 苹果开发者续费以及查看账号到期时间
  7. 微型计算机课设电梯控制8255,东南大学吴健雄学微机课程设计电梯控制器.doc
  8. 【期权课堂】第3课 如何像交易股票那样交易期权?
  9. 正则表达式入门级别详细教程
  10. 关于大型网站技术演进的思考