【数据结构与算法】图最短路径算法 ( Floyed 算法 | 图最短路径算法使用场景 | 求解图中任意两个点之间的最短路径 | 邻接矩阵存储图数据 | 弗洛伊德算法总结 )

本文主要是介绍【数据结构与算法】图最短路径算法 ( Floyed 算法 | 图最短路径算法使用场景 | 求解图中任意两个点之间的最短路径 | 邻接矩阵存储图数据 | 弗洛伊德算法总结 ),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、最短路径
  • 二、图最短路径算法使用场景
  • 三、求解图中任意两个点之间的最短路径
  • 四、邻接矩阵存储图数据
  • 五、只允许经过 1 号点中转得到任意两点之间的最短路径
  • 六、在之前的基础上-只允许经过 1、2 号点中转得到任意两点之间的最短路径
  • 七、在之前的基础上-只允许经过 1、2 、...、n 号点中转得到任意两点之间的最短路径
  • 八、弗洛伊德算法总结

图的最短路径算法 : 有如下四种 ;

  • 弗洛伊德算法 Floyed ;
  • 迪杰斯特算法 Dijstra ;
  • 贝尔曼-弗洛伊德算法 Bellman-Floyed ;
  • SPFA 算法 Shortest Path Faster Algorithm ;

本篇博客介绍 弗洛伊德 算法 ;





一、最短路径



中 , 结点 之间的 边 带有权值 , 则该图就是 带权图 ;


边的 权值 可以理解为 两个结点 之间的 距离 或者 消耗时间 ,

从 结点 A 到 结点 B 有不同的路径 ,

将这些路径的 边 的 权值 相加 , 权值总和最小的路径 , 就是 最短路径 ;


举例说明 : 下图 中 , 求 C4 结点 到 C6 结点 的最短路径 ;
在这里插入图片描述

C4 结点 到 C6 结点的路径 :

  • C4 -> C6 : 权值累加总和为 9 ;
  • C4 -> C5 -> C6 : 权值累加总和为 8 ;
  • C4 -> C3 -> C5 -> C6 : 权值累加总和为 8 ;

其它的路径更远 , 可以看到其最短路径是 后两种 , 最短路径为 8 ;





二、图最短路径算法使用场景



图最短路径算法使用场景 :

  • 管道铺设
  • 线路安装
  • 地图规划




三、求解图中任意两个点之间的最短路径



在这里插入图片描述

假设图中有任意两个点 , A 点 和 B 点 ,

要令 A 到 B 之间的 距离 变短 , 只能 引入 第三个点 K , A 先到 K , 然后从 K 到 B ,

此时 A -> B 的路径 可能 小于 A -> K -> B 的路程 ;


中转点 的 个数 可能需要多个 , A 到 B 可能中间途径多个 中转点 , 使得 两个结点 之间的距离更短 ;


以上图为例 , 从 结点 4 到 结点 3 的直接距离为 12 ,

如果 找一个途经点 , 从 结点 4 先到 结点 1 , 然后从 结点 1 到 结点 3 , 最终的距离为 11 ;


如果 找 2 个途径点 , 节点 4 -> 结点 1 -> 结点 2 -> 结点 3 , 距离为 10 ;


每个顶点 都有可能 缩短 另外两个 顶点 之间的距离 ;





四、邻接矩阵存储图数据



使用 邻接矩阵 存储 下图信息 ;

在这里插入图片描述

下图中 使用 二维数组 int[][] edge 存储邻接矩阵 , 二维数组 元素的值为 两个点 之间的 边 的 权重 ;

如 : edge[1][2] 是 从 结点 1 到 结点 2 之间的 边 的权重 ;


邻接矩阵 取值 :

  • 两个结点之间存在边 : 邻接矩阵 取值 就是这个 边 的权重 ;
  • 两个结点之间不存在边 : 如果 没有可达 的边 , 如 结点 2 -> 结点 1 没有直达的边 , 则距离设置为 无穷大 ;
  • 结点到其本身的距离 : 约定为 0 ;

在这里插入图片描述





五、只允许经过 1 号点中转得到任意两点之间的最短路径



在上述 邻接矩阵 int[][] edge 中 , 求 结点 i 到 结点 j 之间的 最短路径 , 并且只允许从 结点 1 进行中转 ;

结点 i 到 结点 j 的距离为 edge[i][j] ,

结点 i 到 结点 1 的距离为 edge[i][1] , 结点 1 到 结点 j 的距离为 edge[1][j] , 其 总的距离为 edge[i][1] + edge[1][j] ,

如果 edge[i][1] + edge[1][j] < edge[i][j] , 则 结点 i 到 结点 j 之间的距离缩短了 , edge[i][1] + edge[1][j] 就是其当前的 最短路径 ;


// 只允许经过 1 号点中转得到任意两点之间的最短路径
for(int i = 1; i <= n; i++) {for(int j = 1; j <= n; j++) {if(edge[i][j] > edge[i][1] + edge[1][j]) {edge[i][j] = edge[i][1] + edge[1][j];}}
}

更新后的 邻接矩阵 变为下图所示 :

在这里插入图片描述

原来 结点 3 -> 结点 2 的 之间没有边 , 距离为 无穷大 , 现在通过 1 中转 , 3 -> 1 -> 2 的距离为 9 , 距离缩短了 ;

原来 结点 4 -> 结点 2 的 之间没有边 , 距离为 无穷大 , 现在通过 1 中转 , 4 -> 1 -> 2 的距离为 7 , 距离缩短了 ;

原来 结点 4 -> 结点 3 的 之间没有边 , 距离为 12 , 现在通过 1 中转 , 4 -> 1 -> 3 的距离为 11 , 距离缩短了 ;





六、在之前的基础上-只允许经过 1、2 号点中转得到任意两点之间的最短路径



上一个章节中 , 已经求出 只允许经过 1 号顶点时 , 任意两点的 最短路径 ;

本章节中 , 在上一章节的基础上 , 再求 经过 2 号顶点 , 是否能 得到 任意两个 结点 , 结点 i 到 结点 j 之间的 最短路径 ;


算法代码如下 :

// 只允许经过 1 号点中转得到任意两点之间的最短路径
for(int i = 1; i <= n; i++) {for(int j = 1; j <= n; j++) {if(edge[i][j] > edge[i][1] + edge[1][j]) {edge[i][j] = edge[i][1] + edge[1][j];}}
}// 只允许经过 1、2 号点中转得到任意两点之间的最短路径
for(int i = 1; i <= n; i++) {for(int j = 1; j <= n; j++) {if(edge[i][j] > edge[i][1] + edge[1][j]) {edge[i][j] = edge[i][1] + edge[1][j];}}
}

在这里插入图片描述

原来 结点 1 -> 结点 3 的 距离为 6 , 现在通过 2 中转 , 1 -> 2 -> 3 的距离为 5 , 距离缩短了 ;

原来 结点 4 -> 结点 3 的 距离为 11 , 路径为 4 -> 1 -> 3 , 现在再通过 2 中转 , 4 -> 1 -> 2 -> 3 , 新的距离为 10 , 距离缩短了 ;





七、在之前的基础上-只允许经过 1、2 、…、n 号点中转得到任意两点之间的最短路径



经过所有点的遍历 , 也就是经过 1、2 、3、4 号点之后 , 得到的 邻接矩阵 中 , 所有的 任意 两个点之间的距离都是最小距离 ;

代码参考 :

// k 代表结点个数 , 经过 1 ~ n 结点中转 , 每次增加一个点
// 就将 邻接矩阵 中的 最短路径 重新计算一遍 
for(int k = 1; k < n; k++) {for(int i = 1; i < n; i++) {for(int j = 1; j < n; j++) {if(edge[i][j] > edge[i][k] + edge[k][j]) {edge[i][j] = edge[i][k] + edge[k][j];}}}	
}

执行上述算法后 , 邻接矩阵 中的元素值 , 就是对应的 任意两个点 之间的最小距离 ;





八、弗洛伊德算法总结



弗洛伊德算法 可以 计算出 图中 任意两个点 的最短路径 ;

弗洛伊德算法的 时间复杂度是 O ( n 3 ) \rm O(n^3) O(n3) , 因为其嵌套了 3 层 for 循环 ; 结点数量小于 200 , 都可以使用该算法 ;

如果 图 中 , 边的权重 有 负数 , 并且 负数 所在边 与其它的边 组成了一个环 , 则不能使用 弗洛伊德算法 处理 ;

负环示例 :

在这里插入图片描述

这篇关于【数据结构与算法】图最短路径算法 ( Floyed 算法 | 图最短路径算法使用场景 | 求解图中任意两个点之间的最短路径 | 邻接矩阵存储图数据 | 弗洛伊德算法总结 )的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/247230

相关文章

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结