图详解第六篇:多源最短路径--Floyd-Warshall算法(完结篇)

文章目录

  • 多源最短路径--Floyd-Warshall算法
    • 1. 算法思想
    • 2. dist数组和pPath数组的变化
    • 3. 代码实现
    • 4. 测试观察
    • 5. 源码

前面的两篇文章我们学习了两个求解单源最短路径的算法——Dijkstra算法和Bellman-Ford算法

这两个算法都是用来求解图的单源最短路径的算法,区别在于Dijkstra算法不能求解带负权路径的图,而Bellman-Ford算法可以求解带负权路径的图,当然如果图中存在负权回路(负权环)的情况,种情况是求不出最短路径的!Bellman-Ford算法也无能为力,不过我们可以对负权回路的情况进行判定。

那这篇文章我们要再来学习一个求解多源最短路径的算法——Floyd-Warshall算法

那在前面介绍最短路径的问题的时候就已经给大家解释了什么是单源最短路径,什么是多源最短路径,我们再来回顾一下:

单源最短路径指的是从一个源节点出发,计算到其他所有节点的最短路径。也就是说,在单源最短路径问题中,只需要确定一个起点,然后计算该起点到图中所有其他节点的最短距离。
多源最短路径则是在图中计算任意两个节点之间的最短路径。换言之,需要求解所有可能的起点和终点之间的最短路径。

多源最短路径–Floyd-Warshall算法

Floyd-Warshall算法是一种解决多源最短路径问题(任意两点间的最短路径)的算法。

Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法(可以求解带负权的图)。
该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。

当然:

我们前面学的Dijkstra算法和Bellman-Ford算法,它们是用来求单源最短路径的,但是我们如果以所有的顶点为起点都走一遍Dijkstra/Bellman-Ford算法的话,其实也可以得到任意两点间的最短距离。
不过呢,Dijkstra算法的话不可以求解带负权路径的图,而Bellman-Ford算法呢效率又有点低。

1. 算法思想

Floyd算法考虑的是一条最短路径的中间节点

设k是最短路径p的一个中间节点,那么从i到j的最短路径p就被分成i到k和k到j的两段最短路径p1,p2。
p1是从i到k且中间节点属于{1,2,…,k-1}取得的一条最短路径;p2是从k到j且中间节点属于{1,2,…,k-1}取得的一条最短路径
在这里插入图片描述

那它这里如何去求i到j(i,j都可以是任意顶点)最短路径p呢?

在这里插入图片描述
即Floyd算法本质是三维动态规划,D[i][j][k]表示从点i到点j只经过0到k个点最短路径,然后建立起转移方程,然后通过空间优化,优化掉最后一维度,变成一个最短路径的迭代算法,最后即得到所有点的最短路。

给大家简单解释一下上面原理中的这个公式,在动态规划中应该叫状态转移方程:

Di,j,k表示从i到j的最短路径,该路径经过的中间结点是剩余的结点组成的集合中的结点,假设经过k个结点,编号为1…k,然后这里就分为了两种情况:

  1. 如果路径经过了结点k,那么ij的距离就等于ik的距离加上kj的距离,然后剩余就经过k-1个点
  2. 如果不经过结点k,那ij的距离就等于i到j经过k-1个点(不包括k)的距离
    在这里插入图片描述
    那i到j的最短路径就等于这两种情况中的最小值
    在这里插入图片描述

2. dist数组和pPath数组的变化

然后呢在Floyd-Warshall算法中,记录最短路径距离(权值)的dist数组和记录路径(该路径经过了哪些点)的pPath数组我们就要做一些变化了:

前面的两个算法中我们的dist数组和pPath数组都是用了一个一维数组就行了。
但是Floyd-Warshall算法就不一样了,因为前两个算法算的是单源最短路径,而Floyd-Warshall算法是多源最短路径。
因为前面我们都是一个起点,然后求其它顶点到起点的最短路径;而现在是多源,即每个顶点都可以是起点,所以我们要记录每个顶点作为起点时到其它顶点的最短路径距离和路径。
那我们就需要用二维数组了。

3. 代码实现

那下面我们就来尝试写一写Floyd-Warshall算法的代码:

在这里插入图片描述
首先它就不需要给起点了,因为Floyd-Warshall算法求的是多源最短路径,每个顶点都可能是起点,我们都要求
其次,dist数组和pPath数组这里我们要用二维的(vvDist、vvpPath)
然后
在这里插入图片描述
前面的这些初始化工作就不多解释了
接着
我们要把图中所有相连的边的信息直接更新一下,因为上面我们说了那个公式叫做状态转移方程,而这里初始化更新的结果就作为起始状态,后面通过状态转移方程不断更新得到最终结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
那然后下面我们就根据状态转移方程更新就行了
在这里插入图片描述

🆗,搞定!

4. 测试观察

那下面我们再加一个打印权值和路径的二维数组的代码,因为上面那个例子也是把每一步对应的两个二维数组(矩阵)画了出来,我们可以打印(每个顶点作为中间结点更新之后的都打印一下)出来观察对比一下:

在这里插入图片描述
这段代码很简单,没什么解释的

然后我们来测试一下:

在这里插入图片描述
这个测试用例就对应上面给大家看的例子中的图
运行一下
在这里插入图片描述
当然我们这里不能像他那样横着打印

我可以给大家调整一下:

在这里插入图片描述
当然我们这里没有打印初始时的状态,所以我们从第二个开始对比。
那大家可以自己对照一下,应该是没问题的

那然后呢我们可以把所有任意两点的最短路径打印一下:

在这里插入图片描述
在这里插入图片描述
任意两点的最短路径都有。
对照一下
在这里插入图片描述
大家可以自己对照看一下,应该没什么问题。

5. 源码

void FloydWarshall(vector<vector<W>>& vvDist, vector<vector<int>>& vvpPath)
{// 初始化一下记录路径和权值(距离)的数组size_t n = _vertexs.size();vvDist.resize(n);vvpPath.resize(n);for (size_t i = 0; i < n; ++i){vvDist[i].resize(n, MAX_W);vvpPath[i].resize(n, -1);}//把图中所有相连的边的信息先更新一下(初始状态)for (size_t i = 0; i < n; i++){for (size_t j = 0; j < n; j++){if (i == j){vvDist[i][j] = W();}if (_matrix[i][j] != MAX_W){vvDist[i][j] = _matrix[i][j];vvpPath[i][j] = i;}}}//按照状态转移方程更新ij的最短路径//依次遍历取图中的每个结点作为ij的中间结点去更新ij的最短路径for (size_t k = 0; k < n; k++){//k作为中间结点更新ij最短路径for (size_t i = 0; i < n; i++){for (size_t j = 0; j < n; j++){if (vvDist[i][k] != MAX_W&& vvDist[k][j] != MAX_W&& vvDist[i][k] + vvDist[k][j] < vvDist[i][j]){vvDist[i][j] = vvDist[i][k] + vvDist[k][j];vvpPath[i][j] = vvpPath[k][j];}}}// 打印权值和路径矩阵观察数据//	for (size_t i = 0; i < n; ++i)//	{//		for (size_t j = 0; j < n; ++j)//		{//			if (vvDist[i][j] == MAX_W)//			{//				//cout << "*" << " ";//				printf("%3c", '*');//			}//			else//			{//				//cout << vvDist[i][j] << " ";//				printf("%3d", vvDist[i][j]);//			}//		}//		cout << endl;//	}//	cout << endl;//	for (size_t i = 0; i < n; ++i)//	{//		for (size_t j = 0; j < n; ++j)//		{//			//cout << vvParentPath[i][j] << " ";//			printf("%3d", vvpPath[i][j]);//		}//		cout << endl;//	}//	cout << "=================================" << endl;}
}
void TestFloydWarShall()
{const char* str = "12345";Graph<char, int, INT_MAX, true> g(str, strlen(str));g.AddEdge('1', '2', 3);g.AddEdge('1', '3', 8);g.AddEdge('1', '5', -4);g.AddEdge('2', '4', 1);g.AddEdge('2', '5', 7);g.AddEdge('3', '2', 4);g.AddEdge('4', '1', 2);g.AddEdge('4', '3', -5);g.AddEdge('5', '4', 6);vector<vector<int>> vvDist;vector<vector<int>> vvpPath;g.FloydWarshall(vvDist, vvpPath);//打印任意两点之间的最短路径for (size_t i = 0; i < strlen(str); ++i){g.ptintMinPath(str[i], vvDist[i], vvpPath[i]);cout << endl;}
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/166623.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Rclone连接Onedrive

一、Rclone介绍 Rclone是一款的命令行工具&#xff0c;支持在不同对象存储、网盘间同步、上传、下载数据。 我们这里连接的onedrive&#xff0c;其他网盘请查看官方文档。 注意&#xff1a; 需要先在Windows下配置好了&#xff0c;然后再将rclone配置文件复制到Linux的rclone配…

【编解码】解码字符串中的 UNICODE 字符

前言 由于前后端交互中编码的问题&#xff0c;出现了这样的一串字符&#xff1a; {"share_names":["\u4e2d\u6587\u8def\u5f84"]}出现了unicode编码作为字符串内容的情况&#xff0c;直接用json解析的话会报错&#xff0c;所以在json解析前需要先进行转码…

每日刷题|贪心算法初识

食用指南&#xff1a;本文为作者刷题中认为有必要记录的题目 推荐专栏&#xff1a;每日刷题 ♈️今日夜电波&#xff1a;悬溺—葛东琪 0:34 ━━━━━━️&#x1f49f;──────── 3:17 &#x1f…

[ROS2系列] ORBBEC(奥比中光)AstraPro相机在ROS2进行rtabmap 3D建图

目录 背景&#xff1a; 一、驱动AstraPro摄像头 二、安装rtabmap error1&#xff1a;缺包 三、尝试 四、参数讲解 五、运行 error2: Did not receive data since 5 seconds! 六、效果​编辑 error4: 背景&#xff1a; 1、设备&#xff1a;pc&#xff1b;jeston agx …

使用VGG框架实现从二分类到多分类

一.数据集的准备 与之前的不同&#xff0c;这一次我们不使用开源数据集&#xff0c;而是自己来制作数据集。重点需要解决的问题是对数据进行预处理&#xff0c;如每一个图片的大小均不同&#xff0c;需要进行resize&#xff0c;还需要对每一张图片打标签等操作。 数据集文件 …

【Netty专题】【网络编程】从OSI、TCP/IP网络模型开始到BIO、NIO(Netty前置知识)

目录 前言前置知识一、计算机网络体系结构二、TCP/IP协议族2.1 简介*2.2 TCP/IP网络传输中的数据2.3 地址和端口号2.4 小总结 三、TCP/UDP特性3.1 TCP特性TCP 3次握手TCP 4次挥手TCP头部结构体 3.2 UDP特性 四、总结 课程内容一、网络通信编程基础知识1.1 什么是Socket1.2 长连…

微软 Win11 Dev 预览版 Build 23570 发布,修复文件资源管理器卡顿问题

本心、输入输出、结果 文章目录 微软 Win11 Dev 预览版 Build 23570 发布&#xff0c;修复文件资源管理器卡顿问题前言微软 Win11 Dev 预览版 Build 23570 发布&#xff0c;修复文件资源管理器卡顿问题完整的更新日志[Windows 中的 Copilot][开始菜单][任务栏搜索][设置] 已知问…

面向对象设计原则之依赖倒置原则

目录 定义原始定义进一步的理解 作用实现方法代码示例 面向对象设计原则之开-闭原则 面向对象设计原则之里式替换原则 面向对象设计原则之依赖倒置原则 面向对象设计原则之单一职责原则 定义 依赖倒置原则&#xff08;Dependence Inversion Principle&#xff09;&#xff0c…

【广州华锐互动】全屋智能家电VR虚拟仿真演示系统

在过去的几年中&#xff0c;智能家居的概念已经逐渐进入人们的生活。然而&#xff0c;它的真正潜力和最终形态可能还未被完全发掘。一种新兴的技术&#xff0c;虚拟现实&#xff08;VR&#xff09;&#xff0c;为我们提供了一种全新的方式来理解和体验智能家居。VR公司广州华锐…

FFT64点傅里叶变换verilog蝶形运算,代码和视频

名称&#xff1a;FFT64点verilog傅里叶变换 软件&#xff1a;Quartus 语言&#xff1a;Verilog 代码功能&#xff1a; 使用verilog代码实现64点FFT变换&#xff0c;使用蝶形运算实现傅里叶变换 演示视频&#xff1a;http://www.hdlcode.com/index.php?mhome&cView&…

STM32F4X之GPIO

一、GPIO概述 主控芯片信息如下&#xff1a; 主频&#xff1a;168MHZ内核&#xff1a;ARM-M4FLASH:1MSRAM:192KB引脚&#xff1a;100GPIO:82电压&#xff1a;1.8~3.6V 1.1GPIO概念及其作用 GPIO概念&#xff1a;通用输入输出(General Purpose Input Output)&#xff0c;主要作用…

How to add a jar to a project in eclipse?

Project -> Properties -> Java Build Path -> Libraries -> Add External JARs

前端多媒体处理工具——ffmpeg的使用

写在前面 在前端领域&#xff0c;FFmpeg 是一个非常有用的工具&#xff0c;它提供了多种媒体格式的封装和解封装&#xff0c;包括多种音视频编码、多种协议的流媒体、多种色彩格式转换、多种采样率转换、多种码率切换等。可以在多种操作系统安装使用。 安装 下载FFmpeg 在网…

免费Scrum管理工具-Leangoo领歌

Leangoo领歌是一款永久免费的专业的敏捷开发管理工具&#xff0c;提供端到端敏捷研发管理解决方案&#xff0c;涵盖敏捷需求管理、任务协同、进展跟踪、统计度量等。 
 Leangoo领歌上手快、实施成本低&#xff0c;可帮助企业快速落地敏捷&#xff0c;提质增效、缩短周期、加速…

vue PWA serviceWorker 有新内容时,如何自动刷新内容

vue PWA serviceWorker 有新内容时&#xff0c;如何自动刷新内容 一、问题描述 vue 自带的 pwa 插件可以很方便管理 serviceWorker 的使用&#xff0c;但会有一个问题。 ServiceWorker 的运行机制是这样的&#xff1a; 后台检测到新版本新版 ServiceWorker 下载并安装安装完…

【MySQL】8.0新特性、窗口函数和公用表表达式

文章目录 1. 新增特性2. 移除旧特性2.1 优点2.2 缺点 3. 新特性1&#xff1a;窗口函数3.1 使用窗口函数前后对比3.2 窗口函数分类3.3 语法结构3.4 分类讲解3.4.1 序号函数3.4.1.1 ROW_NUMBER()函数3.4.1.2 RANK()函数3.4.1.3 DENSE_RANK()函数 3.4.2 分布函数3.4.2.1 PERCENT_R…

Qt使用QListWidget实现自定义Item效果

Q&#xff1a;如何在Qt库的基础上&#xff0c;实现自定义控件呢&#xff1f; A&#xff1a;根据官方文档回答&#xff0c;就是继承需实现的控件&#xff0c;然后实现自定义功能。 以下是实现QListWidget控件的自定义item。 先看下最终效果是如何&#xff1a; listItem 主界面U…

linux安装新版本git2、配置github-ssh。(centos、aws)

一、安装Git 1、yum默认版本git #1.安装git sudo yum install git -y #2.确认Git已经安装成功 git --version如果要安装较新版本&#xff0c;可以安装一个repo &#xff0c;但是我这第一次尝试失败了&#xff0c;执行完提示找不到git2u&#xff0c;ius repo也连不上。而且每次…

HTML图像标签

html文件&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>图像标签学习</title> </head> <body> <img src"../resources/image/01.jpg" alt"小狗图…

通过WinSCP实现Windows给Ubuntu(Linux)虚拟机传输数据

要实现传输有几个准备工作需要做 1.在虚拟机运行工具&#xff08;VMware或者其他&#xff09;中设置网络&#xff08;或者网络适配器&#xff09;为桥接模式&#xff08;之前是NAT模式&#xff09; 2.使用ifconfig命令查看虚拟机的网络地址 3.确定虚拟机中安装了ssh 安装 sudo…