算法:最短路径

news2024/11/27 11:49:21

文章目录

  • Dijkstra算法
  • Bellman-Ford算法
  • Floyd-Warshall

本篇总结的是图当中的最短路径算法

Dijkstra算法

单源最短路径问题:给定一个图G = ( V , E ) G=(V,E)G=(V,E),求源结点s ∈ V s∈Vs∈V到图中每个结点v ∈ V v∈Vv∈V的最短路径。Dijkstra算法就适用于解决带权重的有向图上的单源最短路径问题,同时算法要求图中所有边的权重非负。一般在求解最短路径的时候都是已知一个起点和一个终点,所以使用Dijkstra算法求解过后也就得到了所需起点到终点的最短路径。针对一个带权有向图G,将所有结点分为两组SQS是已经确定最短路径的结点集合,在初始时为空(初始时就可以将源节点s放入,毕竟源节点到自己的代价是0),Q 为其余未确定最短路径的结点集合,每次从Q 中找出一个起点到该结点代价最小的结点u ,将uQ 中移出,并放入S中,对u 的每一个相邻结点v 进行松弛操作。松弛即对每一个相邻结点v ,判断源节点s到结点u的代价与uv 的代价之和是否比原来sv 的代价更小,若代价比原来则要将sv 的代价更新为su 与u 到v 的代价之和,否则维持原样。如此一直循环直至集合Q 为空,即所有节点都已经查找过一遍并确定了最短路径,至于一些起点到达不了的结点在算法循环后其代价仍为初始设定的值,不发生变化。Dijkstra算法每次都是选择V-S中最小的路径节点来进行更新,并加入S中,所以该算法使用的是贪心策略

Dijkstra算法存在的问题是不支持图中带负权路径,如果带有负权路径,则可能会找不到一些路径的最短路径

那么下面用一个例子来说明这个算法:

在这里插入图片描述

在这里插入图片描述
Dijkstra算法是解决最短路劲中的常用算法,它的局限性在于不能求带有负权的图

下面是代码实现的过程

		// Dijkstra求最短路径
		void Dijkstra(V vertex, vector<W>& warray, vector<size_t>& parentpath)
		{
			size_t srci = GetVertexsIndex(vertex);
			// 创建两个数组,一个数组用来存最短权值,一个数组存路径,并进行数据的初始化
			warray.resize(_vertexs.size(), W_MAX);
			parentpath.resize(_vertexs.size(), W_MAX);
			warray[srci] = W();
			parentpath[srci] = srci;
			// 用一个vector用来记录每个顶点是否都被选过了
			vector<bool> check(_vertexs.size(), false);

			// 每个顶点都用贪心算法找一次
			for (size_t num = 0; num < _vertexs.size(); num++)
			{
				// 定义一些变量,用来寻找到某个顶点最短的路径值和这个顶点的值
				W minW = W_MAX;
				size_t u = 0;
				// 首先遍历一次全部顶点,寻找从起点到该点路径最短的点
				for (size_t i = 0; i < _vertexs.size(); i++)
				{
					// 如果这个顶点没有被选过,并且这个顶点和起点之间联通,就把它选进来
					if (check[i] == false && warray[i] != W_MAX && warray[i] < minW)
					{
						minW = warray[i];
						u = i;
					}
				}
				// 运行到这里,就找出了到起点位置距离最短的顶点和具体的值,分别为minw和u
				check[u] = true;
				// 此时进行松弛算法,更新这个点到周围延伸出的顶点的路径值
				for (size_t j = 0; j < _vertexs.size(); j++)
				{
					// _vertexs[j]这个顶点经过_vertexs[u]到起点的距离小于它现在的值,那么就进行更新
					if (check[j] == false && _matrix[u][j] != W_MAX && warray[u] + _matrix[u][j] < warray[j])
					{
						// 此时说明可以进行更新
						warray[j] = warray[u] + _matrix[u][j];
						parentpath[j] = u;
					}
				}
			}
		}

为了便于观察,实现一个打印路径的逻辑算法,思路还是很简单的


		// 打印最短路径的逻辑算法
		void PrinrtShotPath(const V& src, const vector<W>& dist, const vector<size_t>& parentPath)
		{
			size_t srci = GetVertexsIndex(src);
			for (size_t i = 0; i < _vertexs.size(); ++i)
			{
				if (i == srci)
					continue;
				vector<size_t> path;
				size_t parenti = i;
				while (parenti != srci)
				{
					path.push_back(parenti);
					parenti = parentPath[parenti];
				}
				path.push_back(srci);
				reverse(path.begin(), path.end());
				for (auto pos : path)
				{
					cout << _vertexs[pos] << "->";
				}
				cout << dist[i] << endl;
			}
		}

Dijkstra算法的时间复杂度是O(N^2)

Bellman-Ford算法

Dijkstra算法只能用来解决正权图的单源最短路径问题,但有些题目会出现负权图。这时这个算法就不能帮助我们解决问题了,而bellmanford算法可以解决负权图的单源最短路径问题。它的优点是可以解决有负权边的单源最短路径问题,而且可以用来判断是否有负权回路。它也有明显的缺点,它的时间复杂度 O(N*E) (N是点数,E是边数)普遍是要高于Dijkstra算法O(N²)的。像这里如果我们使用邻接矩阵实现,那么遍历所有边的数量的时间复杂度就是O(N^3),这里也可以看出来Bellman-Ford就是一种暴力求解更新

在这里插入图片描述
简单来说,Bellman算法就是一个暴力枚举的过程,虽然时间复杂度高,但是确实是可以枚举出带有负权的图的结果

		// Bellman求最短路径
		bool Bellman(V vertex, vector<W>& warray, vector<size_t>& parentpath)
		{
			size_t srci = GetVertexsIndex(vertex);
			// 创建两个数组,一个数组用来存最短权值,一个数组存路径,并进行数据的初始化
			warray.resize(_vertexs.size(), W_MAX);
			parentpath.resize(_vertexs.size(), W_MAX);
			warray[srci] = W();
			parentpath[srci] = srci;

			// 有k条边,每次更新一次边就刷新一次
			for (int k = 0; k < _vertexs.size(); k++)
			{
				for (int i = 0; i < _vertexs.size(); i++)
				{
					for (int j = 0; j < _vertexs.size(); j++)
					{
						// 运行到这里进入到邻接矩阵中,分别看一下路径长短并进行更新
						// 如果从起点到j的距离大于从起点到i,再从i到j的整体距离,此时就进行更新
						if (_matrix[i][j] != W_MAX && warray[i] + _matrix[i][j] < warray[j])
						{
							warray[j] = warray[i] + _matrix[i][j];
							parentpath[j] = i;
						}
					}
				}
			}

			// 判断一下是不是带有负权的环,如果带有环,就返回false
			for (int i = 0; i < _vertexs.size(); i++)
			{
				for (int j = 0; j < _vertexs.size(); j++)
				{
					// 如果还是能更新,就说明带负权的环了
					if (_matrix[i][j] != W_MAX && warray[i] + _matrix[i][j] < warray[j])
					{
						return false;
					}
				}
			}

			return false;
		}

Floyd-Warshall

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

Floyd算法考虑的是一条最短路径的中间节点,即简单路径p={v1,v2,…,vn}上除v1vn的任意节点。

kp的一个中间节点,那么从i到j的最短路径p就被分成ikkj的两段最短路径p1p2p1是从ik且中间节点属于{1,2,…,k-1}取得的一条最短路径。p2是从kj且中间节点属于{1,2,…,k-1}取得的一条最短路径。

在这里插入图片描述

void FloydWarShall(vector<vector<W>>& vvDist, vector<vector<int>>& vvParentPath)
		{
			size_t N = _vertexs.size();
			vvDist.resize(N);
			vvParentPath.resize(N);
			// 初始化权值和路径矩阵
			for (size_t i = 0; i < N; ++i)
			{
				vvDist[i].resize(N, MAX_W);
				vvParentPath[i].resize(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];
						vvParentPath[i][j] = i;
					}
					else
					{
						vvParentPath[i][j] = -1;
					}
					if (i == j)
					{
						vvDist[i][j] = 0;
						vvParentPath[i][j] = -1;
					}
				}
			}
			// 依次用顶点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 比 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];
							vvParentPath[i][j] = vvParentPath[k][j];
						}
					}
				}
			}
		}

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

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

相关文章

H266/VVC标准的编码结构介绍

概述 CVS&#xff1a; H266的编码码流包含一个或多个编码视频序列&#xff08;Coded Video Swquence&#xff0c;CVS&#xff09;&#xff0c;每个CVS以帧内随机接入点&#xff08;Intra Random Access Point&#xff0c; IRAP&#xff09;或逐渐解码刷新&#xff08;Gradual …

力扣题:数字与字符串间转换-12.18

力扣题-12.18 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;38. 外观数列 解题思想&#xff1a;进行遍历然后对字符进行描述即可 class Solution(object):def countAndSay(self, n):""":type n: int:rtype: str""&quo…

小程序静默登录-登录拦截实现方案【全局loginPromis加页面拦截】

实现效果&#xff1a; 用户进入小程序访问所有页面运行onload、onShow、onReady函数时保证业务登录态是有效的 实现难点&#xff1a; 由于小程序的启动流程中&#xff0c;页面级和组件级的生命周期函数都不支持异步阻塞&#xff1b;因此会造成一个情况&#xff0c;app.onLau…

【从零开始学习--设计模式--策略模式】

返回首页 前言 感谢各位同学的关注与支持&#xff0c;我会一直更新此专题&#xff0c;竭尽所能整理出更为详细的内容分享给大家&#xff0c;但碍于时间及精力有限&#xff0c;代码分享较少&#xff0c;后续会把所有代码示例整理到github&#xff0c;敬请期待。 此章节介绍策…

植物分类-PlantsClassification

一、模型配置 一、backbone resnet50 二、neck GlobalAveragePooling 三、head fc 四、loss type‘LabelSmoothLoss’, label_smooth_val0.1, num_classes30, reduction‘mean’, loss_weight1.0 五、optimizer lr0.1, momentum0.9, type‘SGD’, weight_decay0.0001 六、sche…

磁力计LIS2MDL开发(3)----九轴姿态解算

磁力计LIS2MDL开发.3--九轴姿态解算 概述视频教学样品申请完整代码下载使用硬件欧拉角万向节死锁四元数法姿态解算双环PI控制器偏航角陀螺仪解析代码 概述 LIS2MDL 包含三轴磁力计。 lsm6ds3trc包含三轴陀螺仪与三轴加速度计。 姿态有多种数学表示方式&#xff0c;常见的是四元…

【运维笔记】mvware centos挂载共享文件夹

安装mvware-tools 这里用的centos安装 yum install open-vm-tools 设置共享文件夹 依次点击&#xff1a;选项-共享文件夹-总是启用-添加&#xff0c;安装添加向导操作添加自己想共享的文件夹后。成功后即可在文件夹栏看到自己共享的文件夹 挂载文件夹 临时挂载 启动虚拟机&…

lvs-nat部署

LVS负载均衡群集部署——NAT模式 实验环境&#xff1a; 负载调度器&#xff1a;内网关 lvs&#xff0c;ens33&#xff1a;172.16.23.10&#xff1b;外网关&#xff1a;ens36&#xff1a;12.0.0.1 Web服务器1&#xff1a;172.16.23.11 Web服务器2&#xff1a;172.16.23.12 NFS…

【Spring】09 BeanClassLoaderAware 接口

文章目录 1. 简介2. 作用3. 使用3.1 创建并实现接口3.2 配置 Bean 信息3.3 创建启动类3.4 启动 4. 应用场景总结 Spring 框架为开发者提供了丰富的扩展点&#xff0c;其中之一就是 Bean 生命周期中的回调接口。本文将聚焦于其中的一个接口 BeanClassLoaderAware&#xff0c;介…

数据仓库与数据挖掘小结

更加详细的只找得到pdf版本 填空10分 判断并改错10分 计算8分 综合20分 客观题 填空10分 判断并改错10分--错的要改 mooc中的--尤其考试题 名词解释12分 4个&#xff0c;每个3分 经常碰到的专业术语 简答题40分 5个&#xff0c;每道8分 综合 画roc曲线 …

机器视觉技术与应用实战(开运算、闭运算、细化)

开运算和闭运算的基础是膨胀和腐蚀&#xff0c;可以在看本文章前先阅读这篇文章机器视觉技术与应用实战&#xff08;Chapter Two-04&#xff09;-CSDN博客 开运算&#xff1a;先腐蚀后膨胀。开运算可以使图像的轮廓变得光滑&#xff0c;具有断开狭窄的间断和消除细小突出物的作…

C语言数据结构-----二叉树(3)二叉树相关练习题

前言 前面详细讲述了二叉树的相关知识&#xff0c;为了巩固&#xff0c;做一些相关的练习题 文章目录 前言1.某二叉树共有 399 个结点&#xff0c;其中有 199 个度为 2 的结点&#xff0c;则该二叉树中的叶子结点数为&#xff1f;2.下列数据结构中&#xff0c;不适合采用顺序存…

【MySQL】MySQL表的操作-创建查看删除和修改

文章目录 1.创建表2.查看表结构3.修改表4.删除表 1.创建表 语法&#xff1a; CREATE TABLE table_name (field1 datatype,field2 datatype,field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎;说明&#xff1a; field 表示列名datatype 表示列的类型…

GitHub推荐:下载工具-Motrix

项目地址 GitHub - agalwood/Motrix: A full-featured download manager. 项目简介 Motrix是一个开源的下载工具&#xff0c;支持BT下载、Magnet下载。且下载支持最高64个线程&#xff0c;基本可以说下载速度的上限取决于你的带宽。是一款很不错的下载工具。 项目截图

机器视觉技术与应用实战(Chapter Two-03)

2.5 图像滤波和增强 滤波的作用是&#xff1a;图像中包含需要的信息&#xff0c;也包含我们不感兴趣或需要屏蔽的干扰&#xff0c;去掉这些干扰需要使用滤波。 增强的作用是&#xff1a;通过突出或者抑制图像中某些细节&#xff0c;减少图像的噪声&#xff0c;增强图像的视觉效…

介绍strncpy函数

strncpy函数需要引用#include <string.h>头文件 函数原型&#xff1a; char *_Dest 是字符串的去向 char *_Source是字符串的来源 size_t_Count是复制字符串的大小 #include <stdio.h> #include <string.h> int main() { char arr[128] { \0 }; …

『亚马逊云科技产品测评』在当前飞速发展的AI人工智能时代云服务技术哪家强?

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 文章目录 引言一、亚马逊&阿里云发展历史介绍1.1 亚马逊发展历史1.2…

人工智能中的核心概念

1 概述 人工智能英文缩写为AI&#xff0c;是一种由人制造出来的机器&#xff0c;该机器可以模仿人的思想和行为&#xff0c;从而体现出一种智能的反应。 人工智能的产业链分为基础层、技术层、应用层三个层次。 基础层包括&#xff1a;芯片、大数据、算法系统、网络等多项基础…

云计算:Vmware 安装 FreeNAS

目录 一、实验 1.Vmware 安装 FreeNAS 2.配置Web界面 二、问题 1.iSCSI如何限定名称 2.LUN和LVM的区别 一、实验 1.Vmware 安装 FreeNAS &#xff08;1&#xff09;环境准备 VMware Workstation 17 FreeNAS相关安装部署镜像: 官网地址&#xff1a; https://download…

Amazon EC2使用测评

前言 每月/750小时为期 12 个月&#xff0c;通过 AWS 免费套餐使用创建就有750个小时的免费使用额度是真的很香&#xff0c;几乎可以免费使用一年&#xff0c;对于一些喜欢自己搭建博客的用户几乎是发福利&#xff0c;整整一年的免费使用&#xff0c;对于新手来说确实很好&…