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

news2025/1/22 19:10:59

💓博主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.coloradmin.cn/o/1681641.html

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

相关文章

Python代码:三、读入字符串

1、题目 从变量输出开始。请使用input函数读入一个字符串&#xff0c;然后将其输出。 2、代码 import sys ainput() print(a) 3、在sublime运行的结果

从光伏监测到智慧农业,漫途LoRa产品多领域应用

随着物联网&#xff08;IoT&#xff09;技术的飞速发展&#xff0c;对数据传输的稳定性和低功耗特性的需求日益增加。在这一背景下&#xff0c;LoRa凭借更远的传输距离以及出色的抗干扰能力&#xff0c;展现出了显著的优势。这种特性使得LoRa在复杂多变的环境中表现尤为突出&am…

商品服务:SPUSKU规格参数销售属性

1.Object划分 1.PO&#xff08;Persistant Object&#xff09;持久对象 PO就是对应数据库中某个表中的一条记录&#xff0c;多个记录可以用PO的集合。PO中应该不报含任何对数据库的操作 2.DO(Domain Object) 领域对象 就是从现实世界中抽象出来的有形或无形的业务实体。 3…

H3C IPSec配置手记

实验拓扑&#xff1a; 设备型号&#xff1a;H3C MSR36-20 接口地址&#xff1a;ipsec1 g0/0&#xff0c;1.1.1.1 ipsec2 g0/0&#xff0c;1.1.1.2 IPSec1内部地址&#xff1a;172.16.168.0/24 IPSec2内部地址&#xff1a;192.168.168.0/24 目的&#xff1a;建立IPSec隧道&a…

数字化社会的引擎:揭示Facebook的影响力

在当今数字化社会中&#xff0c;社交媒体平台扮演着至关重要的角色&#xff0c;而Facebook作为其中的巨头之一&#xff0c;其影响力不可忽视。本文将深入探讨Facebook的影响力&#xff0c;从多个角度揭示其在数字化社会中的引擎作用。 1. 社交互动的核心平台 Facebook作为社交…

多客陪玩系统源码,线上游戏开黑陪玩,线下预约家政服务,语音陪聊,陪玩成品搭建,源码交付,支持二开,陪玩系统开发

游戏陪玩系统主要的优势就是&#xff0c;只要有手游和网游不断推出&#xff0c;就有钱可赚。为什么呢?因为电竞行业正处于上升发展阶段&#xff0c;而且玩游戏对于现代人来说是很好的一种解压方式&#xff0c;所以在市场和用户需求方面都是有保证的。再加上现代人的社交压力越…

亚马逊Prime Day旺季备货遭遇美国海关查验高峰,应对策略全攻略!

随着全球化贸易的日益繁荣&#xff0c;跨境电商企业在旺季备货时面临着巨大的挑战&#xff0c;尤其是当遇到美国海关查验潮时&#xff0c;如何应对成为众多商家关注的焦点。本文将从分析美国海关查验的原因入手&#xff0c;为商家提供一系列应对策略和建议。 一、美国海关查验潮…

电脑恢复出厂设置怎么弄?让你的电脑焕然一新!

电脑恢复出厂设置是一种常见的操作&#xff0c;它可以帮助用户将电脑恢复到初始状态&#xff0c;清除所有数据和设置&#xff0c;使其恢复到出厂时的状态。这对于解决系统故障、清除个人数据以及重新配置电脑等情况非常有用。可是电脑恢复出厂设置怎么弄呢&#xff1f;本文将介…

碳纳米管须状触嗅觉多模态融合传感器在皮革奢侈品真伪鉴定下的设计探索

一、设计方案 1.传感器选择 触觉传感器&#xff1a;选择基于碳纳米管&#xff08;CNT&#xff09;聚合物的柔性MEMS触觉微传感器&#xff0c;由于碳纳米管具有高度的灵敏度和选择性、柔韧性&#xff0c;可以作为触觉传感器&#xff0c;检测材料的微观结构和机械特性。嗅觉传感…

CSS学习笔记之中级教程(二)

CSS学习笔记之中级教程&#xff08;一&#xff09; 6、CSS 布局 - display: inline-block 与 display: inline 相比&#xff0c;主要区别在于 display: inline-block 允许在元素上设置宽度和高度。 同样&#xff0c;如果设置了 display: inline-block&#xff0c;将保留上下外…

开源标注工具LabelMe的使用

开源标注工具LabelMe使用Python实现&#xff0c;并使用Qt作为其图形界面&#xff0c;进行图像多边形标注。源码地址:https://github.com/labelmeai/labelme &#xff0c;最新发布版本为v5.4.1&#xff0c;它遵循GNU通用公共许可证的条款。 1.Features (1).多边形、矩形、圆形、…

GDPU Java 天码行空12

&#xff08;一&#xff09;实验目的 1、掌握JAVA中多线程的实现方法&#xff1b; 2、重点掌握多线程的同步与通信机制&#xff1b; 3、熟悉JAVA中有关多线程同步与通信的方法 &#xff1b; 4、能使用多线程机制解决实际应用中的线程同步与通信问题。 &#xff08;二&#xf…

抖音小店有订单后怎么发货?实操分享!发货全流程来了

哈喽~我是电商月月 做无货源抖音小店的店铺在出单后怎么发货&#xff1f;今天我就来给大家解答这个问题&#xff0c;其中的注意事项新手商家可以收藏一下&#xff0c;避免犯错 抖音小店的商品出单后&#xff0c;商家在“管理中心-订单管理”页面就能看见所有待处理的订单 一…

haddoop三大核心组件

hadoop三大核心组件分别是hdfs、mapreduce和yarn。 1、hdfs&#xff1a;即分布式文件系统&#xff0c;用于存储hadoop集群中的大量数据。具有高容错性&#xff0c;可跨多个数据节点存储数据&#xff0c;并提供高吞吐量的数据访问&#xff1b; 2、mapreduce&#xff1a;用于大…

羊大师:羊奶健康的成长伴侣

羊大师&#xff1a;羊奶健康的成长伴侣 在追求健康生活的当下&#xff0c;越来越多的人开始关注饮食的营养与健康。羊大师发现在众多天然食品中&#xff0c;羊奶以其独特的营养价值和健康益处&#xff0c;逐渐成为了人们的新宠。特别是对于正在成长发育的孩子们来说&#xff0…

数据结构与算法学习笔记十三---数组的顺序表示和实现(C语言)

目录 前言 一、什么是数组 二、数组的顺序存储 1.定义 2.初始化 3.销毁数组 4.获取指定下标的数据元素 5.给数组中的数据元素赋值 6.完整代码 前言 这篇文章主要介绍数组的顺序存储。 一、什么是数组 数组是由类型相同的数据元素构成的有序集合&#xff0c;每个元素成…

【CTF Web】NSSCTF 3861 [LitCTF 2023]我Flag呢?Writeup(信息收集+源码泄漏+代码审计)

[LitCTF 2023]我Flag呢&#xff1f; 奇怪&#xff0c;放哪里了&#xff0c;怎么看不见呢&#xff1f;&#xff08;初级难度&#xff09; 解法 按下 F12&#xff0c;打开开发者工具。找到 flag。 <!DOCTYPE html> <html><head><meta charset"utf-8&q…

AI应用案例:吸烟打电话行为识别推理

使用百度PaddlePaddle&#xff08;现更名为PaddlePaddle-GPU或PaddlePaddle-CPU&#xff09;框架来构建精准的人员抽烟、打电话动作识别模型&#xff0c;并将其应用于加油站监控场景&#xff0c;你可以遵循以下步骤&#xff1a; 数据准备&#xff1a; 收集抽烟和打电话行为的图…

Originx的创新解法之:应用程序故障篇

Originx并不期望做一个完整覆盖全栈的监控体系&#xff0c;而是利用北极星指标体系标准化找出故障方向&#xff0c;然后联动各种成熟的监控数据形成证据链条&#xff0c;并将各种数据融合在一个故障报告之中。更多信息请参考 Log | Metrics | Trace的联动方式探讨http://mp.wei…