【高阶数据结构(四)】图的最短路径问题

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:高阶数据结构专栏⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习更多数据结构
  🔝🔝


在这里插入图片描述

高阶数据结构

  • 1. 前言
  • 2. 单源最短路径问题
  • 3. dijkstra算法讲解
  • 4. bellman-Ford算法讲解
  • 5. 多源最短路径问题
  • 6. Floyd-Warshall算法讲解
  • 7. 总结

1. 前言

关于图论,无非就是最小生成树问题和最短路径问题. 对于最短路径问题来说, 分为单源最短路径和多源最短路径, 并且图中的权值是否有负数, 对应能使用的算法也不同

本章重点:

本篇文章着重讲解图的单源最短路径之Dijkstra算法和bellman-Ford算法.以及多源最短路径之Floyd-wars hall算法. 文章会着重讲解这些算法的思路, 代码实现部分要靠大家的理解能力了


2. 单源最短路径问题

所谓的单源最短路径,也就是从图中任意一点出发, 到图中每个节点的最短路径,也就是最小的权值和

在这里插入图片描述

对于单源最短路径的求解. 我们一般使用输出型参数. 用两个数组来表示最短路径的权值以及最短路径的路径.

//存储任意点到图中其他点的最短路径的权值vector<W>& dist//记录srci->其他顶点最短路径父顶点数组vector<int>& parentPath

第一个数组很好理解. 图中的顶点会简化成为数组中的元素. 所以dist数组中的dist[i]=j.代表顶点i到srci的最短路径的权值. 不好理解的是第二个数组. 它存储的是最短路径的父顶点. 什么意思呢? 请看下图:

在这里插入图片描述


3. dijkstra算法讲解

针对一个带权有向图G,将所有结点分为两组S和Q,S是已经确定最短路径的结点集合,在初始时为空(初始时就可以将源节点s放入,毕竟源节点到自己的代价是0),Q 为其余未确定最短路径的结点集合,每次从Q 中找出一个起点到该结点代价最小的结点u ,将u 从Q 中移出,并放入S 中,对u 的每一个相邻结点v 进行松弛操作。

松弛即对每一个相邻结点v ,判断源节点s到结点u 的代价与u 到v 的代价之和是否比原来s 到v 的代价更小,若代价比原来小则要将s 到v 的代价更新为s 到u 与u 到v 的代价之和,否则维持原样。如此一直循环直至集合Q 为空,即所有节点都已经查找过一遍并确定了最短路径,至于一些起点到达不了的结点在算法循环后其代价仍为初始设定的值,不发生变化

定义很抽象,现在来看看实图:

在这里插入图片描述

从S开始,s->y是最短路径了, 就以y为起点(y的值被更新为5)更新与y相连的t,z,x. 同时s->t也被更新为10. y->t小于s->t. 所以将t重新更新为8. x,z也是同理. 第二次更新完. s->z最短.就以z为起点更新与z相连的x.以此类推.直到所有顶点都在集合S中.

话不多说,上代码:

void Dijkstra(const V& src, vector<W>& dist, vector<int>& pPath)//Dijkstra算法求解最短路径,两个数组,一个存储两个点之间的最小权值(从src点,到图中其他的点),另一个存父路径节点下标
{size_t srci = GetIndex(src);size_t n = _vertex.size();dist.resize(n, MAX_W);pPath.resize(n, -1);dist[srci] = W();pPath[srci] = srci;vector<bool> check(n, false);//此数组中存放已经确定了的最短路劲的节点for(int j=0;j<n;j++)//n个节点,一共会更新n次,也可以判断check数组中的元素是否全为true{//选最短路径的顶点更新其他路径(不在s中)int u = 0;//最小的点的下标W minu = MAX_W;//最小的点的权值for (int i = 0; i < n; i++)//选择dist数组中权值最小的,作为起始点来进行松弛操作{if (check[i] == false && dist[i] < minu){u = i;minu = dist[i];check[i] = true;}}//进行松弛更新,srci->u, u->其他顶点(v), srci->v就可以更新出来for (int v = 0; v < n; v++)//从0到n,把与u点相连的所有顶点都找出来更新{if (_edge[u][v] != MAX_W && dist[u] + _edge[u][v] < dist[v])//若srci->u+u->v的距离小于dist[v]的大小,则更新他{dist[v] = dist[u] + _edge[u][v];pPath[v] = u;}}}
}

此算法只适用于不带负权路径的图
若有不懂,欢迎私信


4. bellman-Ford算法讲解

Dijkstra算法只适用于不带负权路径的图, 具体的原因可以参考这篇文章: 负权路径带来的后果

显而易见, bellman算法可以解决带负权路径的图

说白了此算法就是一个暴力求解的过程, 它的时间复杂度是O(N^3). 它的思路就是以所有顶点为起始点,更新所有相连的边

在这里插入图片描述

更新次序就是(t,z),(t,y),(t,z),(y,x),(y,z), (z,x), (z,s), (s,t), (s,y). 更新(y,z)时,由于更新后的值是7+9=16>2,所以不会更新这条边. 其他更新边也是同理. 但是这样暴力更新一次并不能解决问题,因为假如只更新一次, (s,t)的值就是6, 但是显而易见, s->y->x->t的权值是2,要小于6. 出现这种情况的原因是, 还没有以x为起始点进行更新其他点时, 根本就不知道x->t这条路. 所以我们需要以所有点为起始点更新n次,n是顶点的数量

上代码:

bool BellmanFord(const V& src, vector<W>& dist, vector<int>& pPath)//贝尔曼-福特算法求解最短路径
{size_t srci = GetIndex(src);size_t n = _vertex.size();dist.resize(n, MAX_W);pPath.resize(n, -1);dist[srci] = W();//用i->j,图中的所有边去更新for (int k = 0; k < n; k++)//再套一层循环的原因是,只更新一轮可能会有问题,更新K轮一定不会有问题{bool check = false;for (int i = 0; i < n; i++){for (int j = 0; j < n; j++){if (_edge[i][j] != MAX_W && dist[i] + _edge[i][j] < dist[j])//若这条边存在,并且从i->j要少于直接0->j{dist[j] = dist[i] + _edge[i][j];pPath[j] = i;check = true;}}}if (check == true) breal;}//有可能K轮循环后,会形成闭环return true;
}

很明显它的时间复杂度是O(N^3)


5. 多源最短路径问题

说白了就是任意两点之间的最短路径
Floyd-Warshall算法就是解决方法之一

和单源最短路径算法的思路相似. 这里需要用到两个数组, 只不过这里是用两个二维数组, 一个二维数组存储顶点i->j的最短路径值, 另外一个数组存储 (i,j)的父节点下标. i,j是最短路径的中间某节点


6. Floyd-Warshall算法讲解

Floyd算法考虑的是一条最短路径的中间节点,即简单路径p={v1,v2,…,vn}上除v1和vn的任意节
点。设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}取得的一条最短路径

在这里插入图片描述

在这里插入图片描述

你可能觉得很抽象,下面来个实际案例:

在这里插入图片描述
在这里插入图片描述

上代码:

void FloydWarShall(vector<vector<W>>& vvDist, vector<vector<int>>& vvpPath)//多源最短路径求解问题(任意两点的最短路径),数组vvDist中包含了所有点的最短距离
{size_t N = _vertex.size();vvDist.resize(N);vvpPath.resize(N);for (int i = 0; i < N; i++){vvDist[i].resize(N, MAX_W);vvpPath[i].resize(N, -1);}//把直接相连的边给更新一下,后续就不需要_edge数组了for (int i = 0; i < N; i++){for (int j = 0; j < N; j++){if (_edge[i][j] != MAX_W)//直接相连{vvDist[i][j] = _edge[i][j];vvpPath[i][j] = i;//起点是i,目前的父路径暂时是i(后面可能会是K)}if (i == j)vvDist[i][j] = W();}}//最短路径的更新,i->j,中间可能经过了k个顶点,i->{其他顶点(最多是N-2)}->j//k作为i,j的中间点,k可以是任意顶点,k可以是1,2,3,任意点,要把所有点拿来更新for (int k = 0; k < N; k++)//虽然是最多只需要走n-2个点,但是这里除掉的两个点我们并不知道是哪两个,所以都需要走一遍{for (int i = 0; i < N; i++){for (int j = 0; j < N; j++){//以K作为中间的去更新i->j的路径if (vvDist[i][k] != MAX_W && vvDist[k][j] != MAX_W && vvDist[i][k] + vvDist[k][j] < vvDist[i][j])//i->k的路径和k->j的路径都存在,并且i->k加上k->j的路径小于i直接到j{vvDist[i][j] = vvDist[i][k] + vvDist[k][j];vvpPath[i][j] = vvpPath[k][j];//这里j的父路径不能直接写成k,因为k->j中间可能还有其他点,比如k->x->y->j,最开始的i,j是在_edge数组中取得,而这里应该是从vvppath[k][j]中取得,需要找跟j相连的上一个顶点}}}}
}

7. 总结

图论总体来说比较抽象,很难理解这些算法的思路. 但是也不用慌张,图论本身就属于加分项, 你知道算法原理即可, 不用会手撕, 换个角度, 面试官也不一定能手撕这些算法.


🔎 下期预告:LRU cache讲解 🔍

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

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

相关文章

第八篇 Asciidoc 输出 All In One HTML 解决图片无法显示问题

问题:我的图片显示不出来了 小明使用 Asciidoc 来记笔记,他将笔记输出为 HTML 文件。小丽向小明借笔记。小明将 Asciidoc 笔记输出为 HTML文件,并拷贝给了小丽。 但是,小丽发现,图片都显示不出来了。 小丽:小明,你给我的笔记,图片都显示不出来啊。 小明:是我给你的…

析构函数详解

目录 析构函数概念特性对象的销毁顺序 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x1f978;&#x1f978;&#x1f978; C语言 &#x1f43f;️&#x1f43f;️&#x1f43f;️ C语言例题 &…

yolov8实战之 .pt 转. tensorRT

1 yolo 训练 1.1修改自己的数据集合 我是有3个类别&#xff0c;差不多这么些数据 1.2 训练 from ultralytics import YOLO # Load a model model YOLO("yolov8m.yaml") # build a new model from scratch #model YOLO(E:/pythonCode/pythonProject1/runs/detec…

风电功率预测 | 基于PSO-BP神经网络实现风电功率预测(附matlab完整源码)

风电功率预测 风电功率预测完整代码风电功率预测 基于粒子群优化算法(Particle Swarm Optimization, PSO)的BP神经网络是一种常见的方法,用于实现风电功率预测。下面是一个基于PSO-BP神经网络实现风电功率预测的一般步骤: 数据准备:收集与风电场发电功率相关的数据,包括…

农林科学SCI期刊,IF=6+,影响力高,对国人非常友好!

一、期刊名称 Crop Journal 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;农林科学 影响因子&#xff1a;6.6 中科院分区&#xff1a;1区 出版方式&#xff1a;开放出版 版面费&#xff1a;$900 三、期刊征稿范围 《作物杂志》是一份双月刊、国际、同…

PHP使用Browsershot进行网页截图

Browsershot是什么 Spatie Browsershot 是一个开源PHP库&#xff0c;它允许开发者在PHP应用程序中生成网页的截图。 这个库特别适用于Laravel框架&#xff0c;但也可以在其他 PHP 应用程序中使用。 主要特点 无头浏览器截图&#xff1a;使用无头版本的 Chrome 或 Chromium 浏…

整理好了!2024年最常见 100 道 Java基础面试题(四十九)

上一篇地址&#xff1a;整理好了&#xff01;2024年最常见 100 道 Java基础面试题&#xff08;四十八&#xff09;-CSDN博客 九十七、Class.forName 和 ClassLoader 的区别&#xff1f; Class.forName 和 ClassLoader 是Java中用于加载类的两个不同的概念&#xff0c;它们在类…

10W 3KVAC隔离 宽电压输入 AC/DC 电源模块 ——TP10AF系列

TP10AF系列输出功率为10W&#xff0c;具有可靠性高、更小的体积、性价比高等特点&#xff0c;广泛用于工控和电力仪器、仪表、智能家居等相关行业。

SMB攻击利用之-mimikatz上传/下载流量数据包逆向分析

SMB协议作为windows环境下最为常见的一种协议,在历史上出现过无数的通过SMB协议进行网络攻击利用的案例,包括针对SMB协议本身以及通过SMB协议实施网络攻击。 本文将介绍一种通过SMB协议的常见利用方式,即向远程主机传输mimikatz,作为我的专栏《SMB攻击流量数据包分析》中的…

Oracle数据块之数据行中的SCN

从Oracle 10g开始&#xff0c;如果在表级别打开ROW DEPENDENCIES&#xff0c;业务数据行发生更改时会在数据块中进行登记。 可以通过DUMP数据块来观察上述SCN&#xff1a; &#xff08;1&#xff09;创建测试表&#xff0c;插入3条测试数据&#xff0c;插入一条提交一次。并调用…

解析建筑装饰乙级资质标准及申请流程

建筑装饰乙级资质标准 资历与信誉 必须具备独立的企业法人资格。社会信誉良好&#xff0c;注册资本不少于100万元人民币。 技术条件 专业技术人员配备齐全、合理&#xff0c;满足相应资质标准中对主要专业技术人员数量和专业的具体要求。通常包括但不限于室内设计、建筑、环境艺…

jar包增量更新分析

jdk自带工具jdeps&#xff0c;可分析class依赖关系&#xff08;依赖的其它类和jar&#xff09;。 团队&#xff0c;可以在此工具结果的基础上再详细分析对比出增量文件&#xff1b; 思路如下&#xff1a; jdeps分别分析出旧包和新包的文件依赖关系。并对比出新增的文件列表、…

前端学习第一课

AJAX 事先说明&#xff0c;这只是记录&#xff0c;并不是从零到一的教学内容&#xff0c;如果想要学习的话&#xff0c;可以跳过本文章了 ok&#xff0c;转回正题&#xff0c;正如上面所说&#xff0c;这只是记录。其实我是有一定的前端基础的&#xff0c;也做过涉及相关的开发…

【工具】macOS、window11访问limux共享目录\共享磁盘,samba服务安装使用

一、samba服务安装 Samba是一个免费的开源软件实现&#xff0c;使得非Windows操作系统能够与Windows系统进行文件和打印服务共享。它实现了SMB/CIFS协议&#xff0c;并且能够在Linux、Unix、BSD等多种系统上运行。 安装 samba&#xff1a; sudo yum install samba配置 samba…

【kali工具】NMAP 高级使用技巧

NMAP 高级使用技巧 6.1.3 NMAP 语法及示例 语法&#xff1a;nmap [Scan Type(s)] [Options] 例 1&#xff1a;使用 nmap 扫描一台服务器 默认情况下&#xff0c;Nmap 会扫描 1000 个最有可能开放的 TCP 端口。 ┌──(root&#x1f480;xuegod53)-[~] └─# nmap 192.168…

【介绍下Python多线程,什么是Python多线程】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

【气象常用】时间序列的线性拟合

效果图&#xff1a; 主要步骤&#xff1a; 1. 数据准备&#xff1a;下载Hadley Centre observations datasets的HadSST数据 可参考【气象常用】时间序列图-CSDN博客 2. 数据处理&#xff1a;计算线性拟合 3. 图像绘制&#xff1a;绘制折线及拟合线&#xff0c;并添加文本 …

Nacos部署选择数据源mysql8.0,启动报错No DataSource Set(终极解决方案)

Nacos部署选择数据源mysql8.0&#xff0c;启动报错No DataSource Set&#xff08;终极解决方案&#xff09; 选择mysql5.7正常&#xff0c;但是选择mysql8.0就报这个错&#xff0c;配置都确认无问题&#xff0c;但就是用不了mysql8.0 排查了好久&#xff0c;发现是数据库字符集…

其他自动化工程师都在偷偷学习AI技术,你再不学就落后了!一篇文章教会你使用AI!

其他自动化工程师都在偷偷学习AI技术&#xff0c;你再不学就落后了&#xff01;一篇文章教会你使用AI&#xff01; 哈喽&#xff0c;大家好&#xff0c;我是小叔。了解小叔的朋友都清楚&#xff0c;我从来都不是标题党&#xff0c;我只会用美女图片来吸引你们&#x1f602;&am…

python 六句话让电脑告诉你,刚才插入的串口编号

六句话让电脑告诉你&#xff0c;我的串口号 第一步&#xff0c;安装python 编译器以及pyserial 模块第二步&#xff0c;写入代码 import serial.tools.list_ports usart_list list(serial.tools.list_ports.comports()) input("Please insert your serial port:")…