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

最短路径问题:从在带权有向图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);

			//自身就是0
			dist[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->j
				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])
						{
							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)。能够求解任意俩顶点的最短路径。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实操 - openstack的自动化部署

一 、使用openstack自带的工具packstack部署allinone模式 此模式将所有的服务装在一个虚机中&#xff0c;用来测试 1.克隆一台虚拟机&#xff08;配置好七项&#xff09;virt-clone 2.下载openstack-packstack之前删除mariadb所有相关内容(可选项) #rpm -qa | grep mariad…

二叉树与堆

目录 1.树概念及结构 1.1树的概念 1.2 树的相关概念 1.3 树的表示 1.4 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 2.二叉树概念及结构 2.1概念 2.2现实中的二叉树&#xff1a; 2.3 特殊的二叉树&#xff1a; 2.4 二叉树的性质 2.5 二叉树的…

kafka生产者

1.原理 2.普通异步发送 引入pom&#xff1a; <dependencies><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>3.0.0</version></dependency><dependency><g…

【信息系统项目管理师】--【信息技术发展】--【现代化创新发展】--【大数据】

文章目录 第二章 信息技术发展2.2 新一代信息技术及应用2.2.3 大数据1.技术基础2.关键技术3.应用和发展 第二章 信息技术发展 信息技术是在信息科学的基本原理和方法下&#xff0c;获取信息、处理信息、传输信息和使用信息的应用技术总称。从信息技术的发展过程来看&#xff0c…

python常用文件操作

1.文件夹创建&#xff0c;删除&#xff0c;重命名&#xff0c;路径连接&#xff0c;文件打开&#xff0c;关闭读写 #文件夹创建 path ./test newpath "./new" #判断文件夹是否存在 ret os.path.exists(path) if ret:pass else:#创建文件夹os.mkdir(path)#文件夹重…

牛客周赛 Round 34 解题报告 | 珂学家 | 构造思维 + 置换环

前言 整体评价 好绝望的牛客周赛&#xff0c;彻底暴露了CF菜菜的本质&#xff0c;F题没思路&#xff0c;G题用置换环骗了50%, 这大概是唯一的亮点了。 A. 小红的字符串生成 思路: 枚举 a,b两字符在相等情况下比较特殊 a, b input().split() if a b:print (2)print (a)pri…

关系型数据库事务的四性ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)

关系型数据库事务的四性ACID:原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isolation&#xff09;和持久性&#xff08;Durability&#xff09; 事务的四性通常指的是数据库事务的ACID属性&#xff0c;包括原子性&…

Find My小风扇|苹果Find My技术与小风扇结合,智能防丢,全球定位

电风扇在我们的日常生活中也是经常会使用到的家电产品&#xff0c;尤其是在炎炎的夏日&#xff0c;风扇能给我们吹来清凉的凉风&#xff0c;如今随身携带的小风扇成为人们出门的必备物品&#xff0c;由于体积小方便经常会被人遗忘在某个地方导致丢失。 在智能化加持下&#x…

官方必读!脚本附赠技术教程系列:麒麟天御安全域管平台V4.0.0服务端云底座部署(2)

1.部署须知 1.1.部署说明 执行本部署操作文档&#xff0c;请用户知悉如下内容后再操作&#xff1a; 仅限用于部署麒麟容器云底座&#xff0c;部署前请准备好相应的物料&#xff1b;部署前请提前准备好集群LICENSE&#xff0c;用于激活容器云底座&#xff08;可使用临时版用于测…

vscode使用restClient实现各种http请求

vscode使用restClient实现各种http请求 一&#xff0c;安装插件 首先&#xff0c;我们要在vscode的扩展中&#xff0c;搜索rest Client&#xff0c;然后安装它&#xff0c;这里我已经安装过了。 安装后&#xff0c;我们就可以使用rest client插件进行http各种操作了。 二&…

动态规划的时间复杂度优化

作者推荐 视频算法专题 本文涉及知识点 动态规划汇总 优化动态规划的时间复杂度&#xff0c;主要有如下几种&#xff1a; 一&#xff0c;不同的状态表示。 比如&#xff1a;n个人&#xff0c;m顶帽子。 第一种方式&#xff1a;dp[i][mask] ,i表示前i个人已经选择帽子&…

听李国武老师讲帕累托图

一、帕累托图是什么&#xff1f; 帕累托图是一种特殊的图表&#xff0c;它以二维的方式展示数据&#xff0c;通过将数据按照两个特定的维度进行分类和排序&#xff0c;帮助我们更好地理解和分析数据。 二、如何使用帕累托图&#xff1f; 确定两个分类维度&#xff1a;首先&am…

力扣--动态规划1014.最佳观光组合

思路分析: 初始化左侧景点的评分为第一个景点的评分&#xff0c;最终结果为0。从第二个景点开始遍历数组。对于每个景点&#xff0c;计算当前观光组合的得分&#xff0c;即当前景点的评分 左侧景点的评分 - 两者之间的距离。更新最终结果为当前得分和之前结果的较大值。更新左…

数据结构:链表的冒泡排序

法一&#xff1a;修改指针指向 //法二 void maopao_link(link_p H){if(HNULL){printf("头节点为空\n");return;}if(link_empty(H)){printf("链表为空\n");return;}link_p tailNULL;while(H->next->next!tail){link_p pH;link_p qH->next;while(q…

探索创意的无尽宇宙——Photoshop 2020,你的视觉魔法棒

在数字艺术的广阔天地中&#xff0c;Photoshop 2020无疑是一颗璀璨的明星。这款由Adobe公司精心打造的图像处理软件&#xff0c;自推出以来&#xff0c;便以其强大的功能和卓越的性能&#xff0c;赢得了全球数百万设计师、摄影师和爱好者的青睐。无论是Mac还是Windows系统&…

UE引擎, 在create blueprint from selection中, 点击select卡死问题处理

1. bug场景 在创建子类时点击select&#xff0c; ue会直接冻结无法点击 2. 解决方案 点击“Edit” -> “Edit Preference” 选择Asset Editor Open Location的选项从默认改为“Main Window”即可解决问题 3. 问题产生的原因 原因是UE的弹窗在拓展显示器或者笔记本显示…

DIY制作耳机壳时使用哪一种胶粘剂性价比最高?

选择性价比最高的胶粘剂需要根据具体的应用场景和需求来确定。不同的胶粘剂有不同的特点和使用范围&#xff0c;因此其性价比也不同。 一般来说&#xff1a; 如果需要快速粘合、透明度高、粘合力强的场景&#xff0c;可以选择UV树脂胶&#xff1b; 如果需要高温、高强度的粘合…

复合式统计图绘制方法(1)

复合式统计图绘制方法&#xff08;1&#xff09; 常用的统计图有条形图、柱形图、折线图、曲线图、饼图、环形图、扇形图。 前几类图比较容易绘制&#xff0c;饼图环形图绘制较难。 在统计图的应用方面&#xff0c;有时候有两个关联的统计学的样本值要用统计图来表达&#xff0…

Webserver解决segmentation fault(core dump)段错问问题

前言 在完成了整个项目后&#xff0c;我用make命令编译了server&#xff0c;当我运行./server文件时&#xff0c;出现了段错误 在大量的代码中找出错因并不是一件容易的事&#xff0c;尤其是对新手程序员来说。而寻找bug的过程就像是侦探调查线索追查凶手一样&#xff0c;我们…

GO语言基础总结

多态&#xff1a; 定义一个父类的指针&#xff08;接口&#xff09;&#xff0c;然后把指针指向子类的实例&#xff0c;再调用这个父类的指针&#xff0c;然后子类的方法被调用了&#xff0c;这就是多态现象。 Golang 高阶 goroutine 。。。。。 channel channel的定义 …