弗洛伊德(Floyd)算法(C/C++)

弗洛伊德算法(Floyd's algorithm),又称为弗洛伊德-沃尔什算法(Floyd-Warshall algorithm),是一种用于在加权图中找到所有顶点对之间最短路径的算法。这个算法适用于有向图和无向图,并且可以处理负权重边,但不能处理负权重循环。

弗洛伊德算法(Floyd-Warshall Algorithm)是一种用于计算图中所有顶点对之间最短路径的动态规划算法。本文将详细介绍弗洛伊德算法的原理,并提供一个C++实现的示例,以帮助读者理解算法的工作原理和编程技巧。

算法原理

弗洛伊德算法的核心思想是通过逐步寻找并更新所有顶点对之间的最短路径来解决问题。算法使用一个距离矩阵来存储顶点之间的距离,并在每一步中考虑通过一个新的中间顶点来更新这些距离。跟上一篇Dijkstra算法一样的原理,也是通过中转点去更新最短距离。不过Floyd算法处理的是多源的最短路问题


算法步骤

  1. 初始化一个距离矩阵,其中dist[i][j]表示顶点i到顶点j的直接距离。如果ij不直接相连,则dist[i][j]为无穷大。
  2. 对于每个顶点k,作为中间顶点,更新dist[i][j]min(dist[i][j], dist[i][k] + dist[k][j])

Floyd是经典三重for循环,所以它的时间复杂度为o(n^3),n是图中顶点的数量。第一层遍历中转点,第二层遍历起点,第三层遍历终点,对于图中点的数量多的情况,Floyd算法的时间复杂度是很高的。

图解算法:

下面我们将以4个点的图进行讲解,图的连边为有向边和无向边的结合。以邻接矩阵的方式进行存储,如果大家喜欢用邻接表存储,也可以使用邻接表,下面介绍两个矩阵,矩阵A表示(i,j)i->j的最短距离,初始化为inf。矩阵B表示i->j路径由i到j的中转点,也就是路径上除去起点的第一个点,初始化为-1。

初始:

按照图中的点距离给其赋值,A矩阵i->i距离都为0,inf为无法到达。B矩阵初始为-1。

第一步:

我们选取一个点(按照顺序选取)把它作为中转点,看看以它为中转点,所能到达的点中有没有产生更小的距离,如果产生了,则更新A矩阵的距离,更新B矩阵的中转点。我们先选取1号点,那么位于1号点的行跟列的值都是不可能变化的,还有就是自己到自己的点也是不会变化的永远是0,图中黄颜色标记的是此步不会改变的点,其他的可能会变。在更新距离的时候我们可以不看图就能更新矩阵,例如下图中2号点到3号点本来为10,我们可以连一个矩阵,以1号点画的两条蓝线为两条边,红色线为剩余2边,我们既然把1号点当作中转点,路径必然为2-1-3,此时距离就是副对角线的顶点值相加2+6=8<10,那么通过1号点绕路的方式距离更短。类似的还有3->2号点,6+2=8<inf。3->4号点,10+6=16<inf。4->3号点,10+6=16<inf。顺便把B矩阵更新完。

更新完后(红色标记为变化的值): 

 第二步:

此时把2号结点作为中转结点,看一看能够更新哪一个最短路径,还是跟上一步一样直接看图更新就可以。如下图,4->1号点,2+4=6<10。1->4号点,2+4=6<10。3->4号点,8+4=12<16。4->3号点,8+4=12<16。对于一些不能更新的值,例如1->3号点,2+8=10>6,这样的则不能更新。

对于B矩阵,要注意3->4跟4->3的路径是相反的,更新是则不能直接修改为2,对于3->4号点第一个中转点还是1号点。更新完后(红色标记为变化的值): 

第三步:

把3号点作为中转结点,跟前几步一样,继续寻找最短距离。经过更新我们发现3号点作为中转点不能更新任意一个距离,所以A、B矩阵不需要更新。在图中,经过验证我们发现3号点中转距离反而变大,所以不更新。

第四步:

把4号点作为中转点,继续更新最短距离。我们发现跟3号点一样,不能更新任何距离,在A矩阵中除了黄色的点之外,所能连起来的矩形,主对角线顶点值相加都比当前值要大。在图中也可以验证,所以不给予更新。

这样我们就更新完所有点,把所有点都当作中转点更新完一遍,这样就完成了Floyd算法,更新时每次按照顺序把点当作中转点,遍历寻找路径的起点,再遍历寻找终点,算法时间复杂度为o(n^3)。

视频讲解可以看一下B站这位UP主的讲解,点击直达


算法实现:

以下是弗洛伊德算法的C++实现示例:

#include <iostream>
#include <vector>
#include <limits>
using namespace std;// 定义图的顶点数
const int N = 100;
// 定义无穷大的初始距离
const int INF = numeric_limits<int>::max();// 弗洛伊德算法的实现
void floydWarshall(vector<vector<int>>& dist) {int n = dist.size();// 遍历所有顶点作为中间顶点for (int k = 0; k < n; k++) {// 遍历所有顶点作为起点for (int i = 0; i < n; i++) {// 遍历所有顶点作为终点for (int j = 0; j < n; j++) {// 如果通过顶点k可以找到更短的路径,则更新dist[i][j]if (dist[i][k] != INF && dist[k][j] != INF && dist[i][k] + dist[k][j] < dist[i][j]) {dist[i][j] = dist[i][k] + dist[k][j];}}}}
}int main() {int n; // 顶点的数量cin >> n;vector<vector<int>> dist(n, vector<int>(n, INF)); // 初始化距离矩阵// 读取邻接矩阵for (int i = 0; i < n; i++) {dist[i][i] = 0; // 自己到自己的距离是0for (int j = i; j < n; j++) {int w;cin >> w;dist[i][j] = w;dist[j][i] = w; // 如果是无向图,需要设置对称的权重}}// 执行弗洛伊德算法floydWarshall(dist);// 打印所有顶点对之间的最短路径for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {if (dist[i][j] == INF) {cout << "INF" << " ";} else {cout << dist[i][j] << " ";}}cout << endl;}return 0;
}

Floyd与Dijkstra算法比较 

迪杰斯特拉算法(Dijkstra's algorithm)和弗洛伊德算法(Floyd-Warshall algorithm)都是图论中用于计算图中最短路径的著名算法。它们在某些方面有相似之处,但在设计和应用上存在显著差异,下面我们将对这两种算法的相同跟不同进行解释。

相同点:
  1. 目的两者都旨在解决最短路径问题。
  2. 适用性:它们都可以用于加权图中的最短路径计算,无论是正权还是负权(只有弗洛伊德算法)。
不同点:
  1. 问题范围:
    • 迪杰斯特拉算法:主要用于单元路径的最短路问题,即从单一源点到所有其他顶点的最短路径。
    • 弗洛伊德算法:解决的是所有顶点对之间的最短路径问题,即计算图中每一对顶点之间的最短路径。
  2. 时间复杂度:
    • 迪杰斯特拉算法:具有较高的效率,时间复杂度为O(V^2)(使用朴素实现)或O((V+E) log V)(使用优先队列优化)。(V顶点E条边)
    • 弗洛伊德算法:时间复杂度为O(V^3),因为它需要计算所有顶点对的最短路径。
  3. 实现方式:
    • 迪杰斯特拉算法:通常使用贪心策略,从一个顶点开始,逐步扩展到邻接顶点,直到找到所有顶点的最短路径。
    • 弗洛伊德算法:使用动态规划,通过三层循环迭代地改进路径长度,直到达到最优解。
  4. 对负权边的处理:
    • 迪杰斯特拉算法:不能处理负权边,因为负权边会破坏算法的贪心选择性质。
    • 弗洛伊德算法:可以处理负权边,但图中不能有负权环,否则最短路径问题没有解。
  5. 初始化:
    • 迪杰斯特拉算法:从源点到其他所有顶点的距离初始化为无穷大,源点到自身的距离为0。
    • 弗洛伊德算法:所有顶点到自身的距离初始化为0,其他顶点间的距离初始化为边的权重或无穷大(如果无直接连接)。

本篇详解Floyd算法,如果想看Dijkstra算法的话,可以看博主上一篇博客,针对于Dijkstra算法的详解:迪杰斯特拉(Dijkstra)算法(C/C++)-CSDN博客

执笔至此,感触彼多,全文将至,落笔为终,感谢大家的支持。  

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

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

相关文章

网络层 VIII(网络层设备——路由器)【★★★★★★】

一、冲突域与广播域 这里的“域”表示冲突或广播在其中发生并传播的区域。 1. 冲突域 冲突域是指连接到同一物理介质上的所有结点的集合&#xff0c;这些结点之间存在介质争用的现象&#xff08;能产生冲突的所有设备的集合&#xff09;。也就是说&#xff0c;若这些设备同时发…

「滚雪球学MyBatis」教程导航帖(已完结)

写在前面 我是bug菌&#xff0c;CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家&#xff0c;C站博客之星Top30&#xff0c;华为云2023年度十佳博主&#xff0c;掘金多年度人气作者Top40&#xff0c;掘金等各大社区平台签约作者&#xff0c;51CTO年度博…

酒茶香链接心灵——探寻现代人幸福生活

科技在飞速发展&#xff0c;人类社会以前所未有的速度向前跃进&#xff0c;物质世界的繁荣达到了前所未有的高度。 然而&#xff0c;这光鲜的背后&#xff0c;却无形中拉远了人与人之间的距离&#xff0c;割裂了传统文化的根脉。 传统文化势弱、“快餐文化”层出不穷&#xff0…

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤&#xff08;UserCF&#xff09;2. 基于物品的协同过滤&#xff08;ItemCF&#xff09;3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过…

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Xilinx FPGA 原语解析(二):IBUFDS差分输入缓冲器(示例源码及仿真)

目录 前言&#xff1a; 一、原语使用说明 二、原语实例化代码模版 三、使用示例 1.设计文件代码 2.仿真文件代码 3.仿真结果 前言&#xff1a; 本文主要参考资料xilinx手册&#xff0c;《Xilinx 7 Series FPGA and Zynq-7000 All Programmable SoC Libraries Guide for…

全志A527 A133 A523 T527 T133 H6 H8应用无法开启后台服务

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改4.1 代码修改方法14.2 代码修改方法24.3 代码修改方法35.彩蛋1.前言 像全志的很多平台,普通的app并不能正常的启动后台的服务,这样对于应用层很困扰,无法启动后台的服务,功能就不能正常使用…

【自考zt】【软件工程】【21.10】

关键字&#xff1a; 软件需求基本性质、软件系统需求挑战、耦合&#xff08;高内容&#xff0c;低无直接&#xff09;、内聚&#xff08;初始化时间&#xff09;、uml包、rup边界类、测试首要目标、单元测试最后工作、性能需求 软件开发本质、软件需求规约三种风格、提炼、用…

【MySQL】MySQL库的操作

目录 创建数据库字符集和效验规则查看系统默认字符集和效验规则查看数据库支持的字符集查看数据库支持的字符集效验规则字符集对数据库的影响 操纵数据库查看数据库显示创建语句修改数据库数据库删除总结 数据库的备份和恢复备份恢复注意事项 查看数据库的连接情况 创建数据库 …

【C++】 Vector

文章目录 1 背景2 什么是 vector&#xff1f;3 vector 特性4 基本函数实现5 基本用法6 例子在 vector 最后移除和插入数据clear() 清除 vector 中所有数据排序访问&#xff08;直接数组访问&迭代器访问&#xff09;二维数组两种定义方法 7、参考 1 背景 C 中的容器&#x…

图片转pdf格式怎么弄?非常值得推荐的图片转PDF方法

图片转pdf格式怎么弄&#xff1f;在数字化办公和日常生活中&#xff0c;将图片转换成 PDF 格式已经成为一种常见而有效的处理方式。这种转换不仅能够将多张图片汇总到一个文件中&#xff0c;从而简化管理和共享的过程&#xff0c;还能够确保图像的原始质量和格式不会因不同设备…

WPS如何查看已添加到词典的单词

WPS Office&#xff08;12.1.0.17827&#xff09; ① 点击文件&#xff0c;在文件中找到选项 ② 找到拼写检查并点击自定义词典 ③ 如果要删除已添加到词典的"错词"&#xff0c;则点击修改 ④ 选择"错词", 点击删除

STM32重定义printf,实现串口打印

在“usart.c”文件中加入以下代码 #ifdef __GNUC__#define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endifPUTCHAR_PROTOTYPE{HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);return ch; }…

快速搞定会议记录,别错过这四款语音识别转文字!

作为一个偶尔需要在办公室做会议记录和采访录音整理的打工人。我表示&#xff0c;真的不想要练手速了&#xff0c;尤其是很多人在讲话的时候并不会在意别人来不来得及记录&#xff0c;只在意自己讲不讲的完&#xff0c;好在后面我找了几款语音识别转文字的工具&#xff0c;可算…

52%回报率背后:GPT-4o量化交易机器人的30天实战传奇

_本文介绍了如何利用GPT-4o&#xff0c;结合量化交易技术创建盈利的交易机器人策略&#xff0c;并通过回溯测试验证这一策略的有效性。 量化交易可以盈利&#xff0c;但只有拥有丰富资源、拥有编码和数学技能的交易者或大型机构才能使用。 但时代变了&#xff01;现在有了 Cha…

idear获取git项目

最近想下载个ruoyi项目来包装简历&#xff0c;结果打开idear总是上一个项目&#xff0c;找不到get for vcs只好自己捣鼓了&#xff0c;顺便记录留着下次用。 步骤&#xff1a; 1. 2. 3.输入我们想访问的地址 eg: 点击克隆&#xff0c;我们就能获取项目到本地了。

【零基础必看的数据库教程】——SQL WHERE 子句

WHERE 子句用于提取那些满足指定条件的记录&#xff0c;过滤记录。 SQL WHERE 语法&#xff1a; SELECT column1, column2, ... FROM table_name WHERE condition; 参数说明&#xff1a; column1, column2, ...&#xff1a;要选择的字段名称&#xff0c;可以为多个字段。如…

网络层ip协议

一.概念 ip协议主要是为了在复杂的网络环境中确定一个合适的路径来传输主机间的数据。简单来说就是用来确定主机的位置。 ip协议中的一些设备如下&#xff1a; 主机: 配有 IP 地址, 但是不进行路由控制的设备;路由器: 即配有 IP 地址, 又能进行路由控制;节点: 主机和路由器的统…

浅谈OLTP 与 OLAP 数据建模的差异

OLTP 与 OLAP&#xff1a;常见工作流 联机分析处理 (OLAP) 和联机事务处理 (OLTP) 是两种主要的数据处理系统。两者之间存在多种差异。 OLTP 系统旨在处理来自多个用户的多个事务&#xff0c;它们通常用于许多应用程序的后端。例如&#xff0c;在线商务网站将使用 OLTP 系统来…

Spring6梳理7——依赖注入之特殊类型属性

目录 ①字面量赋值 ②null值 ③xml实体 ④CDATA节 ①字面量赋值 什么是字面量&#xff1f; int a10; 字面量是在源代码中用来表示固定值的表示法。几乎所有的计算机编程语言都支持基本值的字面量表示&#xff0c;例如整数、浮点数和字符串。许多语言还支持布尔类…