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

💓博主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.rhkb.cn/news/329009.html

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

相关文章

基于springboot实现大学生就业需求分析系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现大学生就业需求分析系统演示 摘要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲…

移动端适配(rem,vw)+响应式布局

1.1视口标签&#xff08;逻辑分辨率和设备匹配&#xff09; 1.2二倍图&#xff08;防止失真&#xff09; 二、适配方案 一、rem 1.1媒体查询 1.2、引入js文件&#xff08;remflexble.js&#xff09; 1.2、less Less 是一种动态样式语言&#xff0c;它扩展了 CSS 的功能&#…

sklearn之线性回归——以上证红利指数为例

文章目录 线性回归概念使用sklearn实现上证中立指数预测内置数据集的加载与处理 外部数据集的加载和处理数据内容数据加载和处理 开始预测分割数据集导入线性回归模型查看线性回归模型的系数绘制预测结果预测效果评估 最终代码 线性回归 线性回归&#xff08;Linear Regressio…

大数据比赛-环境搭建(二)

一、ubuntu安装google 1、下载google的Linux安装版 链接&#xff1a;https://pan.baidu.com/s/1w4Hsa1wbJDfC95fX2vU_1A 提取码&#xff1a;xms6 或者&#xff1a;Google Chrome 64bit Linux版_chrome浏览器,chrome插件,谷歌浏览器下载,谈笑有鸿儒 (chromedownloads.net) …

AI办公自动化:用kimi批量把word转换成txt文本

在Kimichat中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个Python脚本编写的任务&#xff0c;具体步骤如下&#xff1a; 打开文件夹&#xff1a;F:\aword 读取里面docx格式的word文档&#xff0c; 提取word文档中的第一行文字作为txt文本文档的标题…

【Python】使用requests采集数据存入mysql或文件

一、什么是requests requests包是一个使用Python编写的HTTP请求库&#xff0c;使得发送HTTP请求和处理HTTP响应变得更加简单。以下是对requests包的详细介绍&#xff1a; 用途&#xff1a; requests包主要用于与HTTP交互&#xff0c;能够发送HTTP请求和处理HTTP响应。它支持处…

软考--试题六--抽象工厂模式(Abstract Factory)

抽象工厂模式(Abstract Factory) 意图 提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无须指定他们具体的类 结构 适用性 1、一个系统要独立于它的产品的创建、组合和表示时 2、一个系统要由多个产品系统中的一个来配置时 3、当要强调一系列相关的产品对象的设…

【Linux系统编程】第十九弹---进程状态(下)

​​​​​​​ ✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、僵尸进程 2、孤儿进程 3、运行状态 4、阻塞状态 5、挂起状态 6、进程切换 总结 1、僵尸进程 上一弹…

普通人也能创业!轻资产短视频带货项目,引领普通人实现创业梦想

在这个信息爆炸的时代&#xff0c;创业似乎成为了越来越多人的梦想。然而&#xff0c;传统的创业模式 keJ0277 往往伴随着高昂的资金投入和复杂的管理流程&#xff0c;让许多普通人望而却步。然而&#xff0c;现在有一种轻资产短视频带货项目正在悄然兴起&#xff0c;它以其低…

apifox接口调试工具的使用,代替postman

官网链接&#xff1a;Apifox &#xff08;代替postman工具&#xff09; 下载apifox工具 使用步骤 安装本地下载的apifox.exx 登录apifox 接口调用

从“制造”到“智造”:“灯塔”经验助力中国制造业转型升级-转载

作者&#xff1a;Karel Eloot&#xff0c;侯文皓&#xff0c;Francisco Betti&#xff0c;Enno de Boer和Yves Giraud 作为中国实体经济的主体&#xff0c;制造业是推动中国经济发展乃至全球制造业持续增长的重要引擎。站在历史与未来交汇的新起点上&#xff0c;中国制造业将背…

2024.05.14 Diffusion 代码学习笔记

配环境 我个人用的是Geowizard的环境&#xff1a;https://github.com/fuxiao0719/GeoWizard。 出于方便考虑&#xff0c;用的pytorch官方的docker容器&#xff0c;因此python版本&#xff08;3.10&#xff09;和原作者&#xff08;3.9&#xff09;不同&#xff0c;其余都是一…

【极简】docker常用操作

镜像images是静态的 容器container是动态的&#xff0c;是基于镜像的&#xff0c;类似于一个进程。 查看docker images&#xff1a; docker images 或者docker image ls 查看docker container情况&#xff1a;docker ps -a&#xff0c;-a意思是--all 运行一个container: doc…

DCMM(数据管理能力成熟度模型)对企业的价值

随着大数据时代的来临&#xff0c;数据已成为企业发展的重要驱动力。为了有效地管理和利用数据&#xff0c;企业需要建立一套完善的数据管理体系&#xff0c;而DCMM&#xff08;数据管理能力成熟度模型&#xff09;正是这样一个帮助企业构建和优化数据管理能力的框架。 DCMM结构…

LeetCode 235. 二叉搜索树的最近公共祖先

LeetCode 235. 二叉搜索树的最近公共祖先 1、题目 题目链接&#xff1a;235. 二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先表…

表的创建与操作表

1. 创建表 创建表有两种方式 : 一种是白手起家自己添&#xff0c;一种是富二代直接继承. 2. 创建方式1 (1). 必须具备条件 CREATE TABLE权限存储空间 (2). 语法格式 CREATE TABLE IF NOT EXISTS 表名(字段1, 数据类型 [约束条件] [默认值],字段2, 数据类型 [约束条件] [默…

C++ 结构体内存对齐

定义了两个结构体 typedef struct Cmd {uint8_t ua;uint8_t ub;uint8_t uc;uint32_t ue; } Cmd_t;typedef struct Cmd_tag {uint8_t value;uint8_t data[1]; // 将 data 定义为指向 Cmd_t 结构体的指针 } tag_t;在实际使用中&#xff0c;看见前人的代码是&#xff0c;new 一块内…

ArcGIS arcpy代码工具——关于标识码的那些事(查找最大标识码、唯一性检查、重排序、空值赋值)

系列文章目录 ArcGIS arcpy代码工具——批量对MXD文件的页面布局设置修改 ArcGIS arcpy代码工具——数据驱动工具批量导出MXD文档并同步导出图片 ArcGIS arcpy代码工具——将要素属性表字段及要素截图插入word模板 ArcGIS arcpy代码工具——定制属性表字段输出表格 ArcGIS arc…

C 深入指针(4)

目录 一、字符指针变量 1 初始化 2 与字符串数组的区别 二、数组指针变量 1 初始化 2 二维数组传参本质 三、函数指针变量 1 初始化 2 用法 四、typedef关键字 五、函数指针数组 一、字符指针变量 1 初始化 //VS2022 x64 #include <stdio.h> int main() {…

供应链投毒预警 | 开源供应链投毒202404月报发布(含投毒案例分析)

概述 悬镜供应链安全情报中心通过持续监测全网主流开源软件仓库&#xff0c;结合程序动静态分析方式对潜在风险的开源组件包进行动态跟踪和捕获&#xff0c;发现大量的开源组件恶意包投毒攻击事件。在2024年4月份&#xff0c;悬镜供应链安全情报中心在NPM官方仓库&#xff08;…