【数据结构】图——最短路径

最短路径问题:从在带权有向图G中的某一顶点出发,找出一条通往另一顶点的最短路径,最短也就是沿路径各边的权值总和达到最小。

最短路径分为图中单源路径和多源路径。

本文会介绍Dijkstra和Bellman-Ford解决单源路径的问题

Floyd-Warshall解决多路径的问题

单源最短路径--Dijkstra算法

单源问题,对于图中给定的结点u,求出到达各个结点v的路径代价和最小。最小路径可能是直接u->v

也可能是从u->t->v  。

Dijkstra算法就是解决所有边非负权值的最短路径。一般是从给出点u到终点v.

算法的思路

将图中的所有结点可以分为S和Q,S是已经确定最短路径的集合,Q是还没被访问的集合)(初始化时,将s放到集合中)

每一次从Q中选出一个s到Q代价最小的点u,把u从s移出放到S中,对u的相邻v 进行松弛更新。

松弛更新:判断s->u(已经确定了)  u->v的路径是否小于s->v(已经确定的)

如果是的话,就更新s->v

直到更新所有的Q结点

在我们的模拟实现中

保存一张dist[]数组,用来表示s到某点的最短路径

S[]数组用来表示已经确定最短路径的顶点、

pPath用来存储路径前一个顶点的下标

通过画图梳理这个过程

首次初始化时,dist数组s位置是0,其它为无穷,pPath数组s位置是缺省值

第二轮:寻找最短路径s-s 松弛更新出s->t s->y

第三步 选出最小v  s->y(s->s已经被标记过)

松弛更新出s-y-t s-y-x s-y-z

第三轮选边 选出s->z 对z相邻的边松弛更新

s->z->x(13)

s->z->s(14 不更新)

第四轮

选边s->t

第五轮 选出s->x 松弛更新出 x->z(不更新,已经是最短路径)

松弛更新不适合用于带负权值的路径

因为无法确定第一轮的最小值

		void Dijkstra(const V& src, vector<W>& dist, vector<int>& pPath){size_t srci = GetVertexIndex(src);size_t n = _vertexs.size();dist.resize(n, MAX_W);pPath.resize(n, -1);//自身就是0dist[srci] = W();pPath[srci] = srci;//标记容器vector<bool> S(n, false);for (size_t j = 0; j < n; j++){int u = 0;W min = MAX_W;for (size_t i = 0; i < n; i++){if (S[i] == false && dist[i] < min){u = i;min = dist[i];}}S[u] = true;//松弛更新for (size_t v = 0; v < n; v++){if (S[v] == false && _matrix[u][v] != MAX_W &&dist[u] + _matrix[u][v] < dist[v]){dist[v] = dist[u] + _matrix[u][v];pPath[v] = u;}}}}

单源最短路径--Bellman-Ford算法
 

迪杰斯特拉算法不能解决带负权值边的最短路径,贝尔曼福特就采用暴力的方式解决了带负权值边的路径问题,还能判断出回路是否带有负权值。

贝尔曼福特算法的思路是将 srci-> j的路径问题划分为srci-> i  i->j的路径最短问题

对于从源顶点 s  到目标顶点 j 的路径来说,如果存在从源顶点 s 到顶点 i 的路径,还存在一条从顶点 i  到顶点 j  的边,并且其权值之和小于当前从源顶点 s  到目标顶点 j 的路径长度,则可以对顶点 j j 的估计值和前驱顶点进行松弛更新。

是利用已经确定的最短路径srci -i 再加上i->j的边权来更新的最短路径。

确定完所有的顶点后,都必须重新进行更新,因为每一条路径的确定,都有可能影响前一轮的路径更新。故需要遍历K次。K为N次。(因为所有的点都在变,都需要更新。)

初始化时,dist[srci]初始化为缺省值

第一次i==0时,更新出t y

第二次,i==1更新出x z 

i==2时,更新出 x s(不更新,目前是最短路径)

i==3时,更新出t->z

i==4时,用x更新出t

结束第一轮

此时还需要进行下一轮的更新

下一轮再i==3时,更新出t->z=-4

		bool BellmanFord(const V& src, vector<W>& dist, vector<int>& pPath){size_t n = _vertexs.size();size_t srci = GetVertexIndex(src);dist.resize(n, MAX_W);pPath.resize(n, -1);dist[srci] = W();//最多更新k轮for (size_t k = 0; k < n; k++){bool updata = false;cout << "更新第:" << k << "轮" << endl;//srci->i+i->jfor (size_t i = 0; i < n; i++){for (size_t j = 0; j < n; j++){if (_matrix[i][j] != MAX_W && dist[i] + _matrix[i][j] < dist[j]){updata = true;cout << _vertexs[i] << "->" << _vertexs[j] << ":" << _matrix[i][j] << endl;dist[j] = dist[i] + _matrix[i][j];pPath[j] = i;}}}if (!updata)break;}//如果还能更新,就是带负路径for (size_t i = 0; i < n; i++){for (size_t j = 0; j < n; j++){if (_matrix[i][j] != MAX_W && dist[i] + _matrix[i][j] < dist[j]){return false;}}}return true;}

利用程序编写过程和我们的画图一致

贝尔曼福特算法是暴力求解,有相当高的时间复杂度O(N^3),但是能够解决负权值问题。


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

Floyd-Warshall算法是解决任意两点间的最短路径的一种算法。

弗洛伊德算法考虑的是中间最短路径的中间结点,设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}取得的一条最短路径。

Floyd-Warshall算法是一种动态规划算法,其运行时间为o(n^3)与最短路径路径上通常的假设一样,假设权重可以为负,但不能有权重为负的环路

如果存在从顶点 i  到顶点 k  的路径,还存在从顶点 k  到顶点 j  的路径,并且这两条路径的权值之和小于当前从顶点 i 到顶点 j 的路径长度,则可以对顶点 j  的估计值和前驱顶点进行松弛更新。

算法原理:

费洛伊徳算法就是将最短路径分成dist[i][k]+ dist[k][j]与dist[k][j]的问题

算法需要一个二维的vvdist记录最短路径,vvpPath标记父路径

将直接相连的视作最短路径 

进行k次更新(K为n,i和j都在变换)

动态规划更新vvdist

注意vvpPath[i][j]的前一个不再是i而应该是递归的vvpPath[k][j],可能变化一次,也可能变化好多次

		void FloydWarshall(vector<vector<W>>& vvDist, vector<vector<int>>& vvpPath){size_t n = _vertexs.size();vvDist.resize(n, vector<W>(n, MAX_W));vvpPath.resize(n, vector<int>(n, -1));//更新权值和父路径for (size_t i = 0; i < n; i++){for (size_t j = 0; j < n; j++){if (_matrix[i][j] != MAX_W){vvDist[i][j] = _matrix[i][j];vvpPath[i][j] = i;}if (i == j){vvDist[i][j] = W();}}}//O(N^3)//最多更新k次for (size_t k = 0; k < n; k++){for (size_t i = 0; i < n; i++){for (size_t j = 0; j < n; j++){//存在 i->k  k->j 且<dist[i][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;}}

最短路径的总结

迪杰斯特拉算法是基于贪心算法设计,解决非负数权值的路径问题。将顶点分为S和Q,每次都从Q中选出最小的权值u放到s中,然后将u的相邻边进行松弛调整。

贝尔曼福特算法是暴力求解,能求解出带负权值的路径,能判断负权环路。是一个时间复杂度为o(N^3)的算法。要进行K次松弛调整。每一次都是通过dist[i]+_matrix[i][j]更新权值

费洛伊徳算法是动态规划的思想,将路径分为i->k k->j的思想。同样要进行K轮次的更新。复杂度为0(N^3)。能够求解任意俩顶点的最短路径。

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

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

相关文章

好物周刊#40:多功能文件管理器

https://github.com/cunyu1943/JavaPark https://yuque.com/cunyu1943 村雨遥的好物周刊&#xff0c;记录每周看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;每周五发布。 一、项目 1. 中国节假日补班日历 中国节假日、调休、补班日历&#xff0c;ICS 格式…

多输入回归预测|GWO-CNN-LSTM|灰狼算法优化的卷积-长短期神经网络回归预测(Matlab)

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、算法介绍&#xff1a; 灰狼优化算法&#xff1a; 卷积神经网络-长短期记忆网络&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容…

Flink Sql 自定义实现 kudu connector

Flink Sql 自定义实现 kudu connector 原理实现 众所周知啊&#xff0c;flinksql 中与其他的存储做数据的传输连接的时候&#xff0c;是需要有独特的连接器的&#xff0c;mysql redis es hbase kudu &#xff0c;不同的存储他们自己使用的协议与操作都不一样&#xff0c;所以需…

(详细使用指南)Linux下交叉编译带ffmpeg的opencv并移植到RK3588等ARM端

一 问题背景 瑞芯微RK3588等嵌入式板作为边缘端设备为算法模型的部署提供了便利&#xff0c;目前很多分类或好检测模型针对边缘端做了优化或量化&#xff0c;使得在边缘端也能达到实时稳定的识别和检测效果。 但嵌入式设备普遍的flash emmc不大&#xff0c;一般在32G左…

git之分支管理

一.理解分支 我们看下面这张图片&#xff1a; 在版本回退⾥&#xff0c;你已经知道&#xff0c;每次提交&#xff0c;Git都把它们串成⼀条时间线&#xff0c;这条时间线就可以理解为是⼀个分⽀。截⽌到⽬前&#xff0c;只有⼀条时间线&#xff0c;在Git⾥&#xff0c;这个分⽀…

kuka示教器嵌套UR界面操作ros中rviz的UR机器人

摘要 本例展示了用QT增加一个网页视图&#xff0c;背景是kuka示教器界面&#xff0c;中间增加UR的VNC网页界面显示。本人博客中一起有写过ros2运行UR的操作。 ros2 UR10仿真包运行_基于ros的ur仿真-CSDN博客 效果如下&#xff1a; 1.打开UR机器人的ros2仿真文件 sudo su ros2…

32RTCBKP

目录 一.时间戳 二.BKP简介 三&#xff0e;RTC外设简介 ​编辑四&#xff0e;相关寄存器 五.相关函数 六.代码实现 (1)读写备份寄存器 (2)实时时钟 一.时间戳 最早在Unix系统上使用 Linux,Windos,安卓的底层计时系统 好处&#xff1a; 1.简化硬件电路&#xff0c…

c++的类型转换方法

一、静态类型转换&#xff08;static_cast&#xff09; 静态类型的转换主要用于基本类型之间的转换&#xff0c;比如int类型转换为double类型。但是static_cast也可以支持上下行的转换&#xff08;存在继承关系之间的转换&#xff09; 基本类型之间的转换举例 上下行转换的举…

《Docker 简易速速上手小册》第8章 Docker 在企业中的应用(2024 最新版)

文章目录 8.1 Docker 在开发环境中的应用8.1.1 重点基础知识8.1.2 重点案例&#xff1a;Python Web 应用开发环境8.1.3 拓展案例 1&#xff1a;Python 数据分析环境8.1.4 拓展案例 2&#xff1a;Python 自动化测试环境 8.2 Docker 在生产环境的实践8.2.1 重点基础知识8.2.2 重点…

174基于matlab的雷达数字信号处理

基于matlab的雷达数字信号处理。该程序具备对雷达目标回波的处理能力&#xff0c;能够从噪声中将目标检测出来&#xff0c;并提取目标的距离、速度、角度信息。有相应的试验文档。程序已调通&#xff0c;可直接运行。 174 雷达数字信号处理 目标检测出来 (xiaohongshu.com)

STM32单片机基本原理与应用(八)

温度传感器实验 实验内容&#xff1a; 单片机通过代码模拟1-Wire总线并对DS18B20进行读写&#xff0c;并在TFTLCD屏幕上显示当前实时温度。 电路原理图&#xff1a; 1-Wire总线 1-Wire总线&#xff1a;即单总线协议&#xff0c;采用单根信号线&#xff0c;既传输时钟&#…

高等数学(无穷小与无穷大)

目录 一、无穷小 二、无穷大 三、无穷小与无穷大的关系 四、无穷小量的阶的比较 一、无穷小 二、无穷大 三、无穷小与无穷大的关系 四、无穷小量的阶的比较