最短路径算法(算法篇)

算法之最短路径算法

最短路径算法

概念

  • 考查最短路径问题,可能会输入一个赋权图(也就是边带有权的图),则一条路径的v1v2…vN的值就是对路径的边的权求和,这叫做赋权路径长,如果是无权路径长就是单纯的路径上的边数

  • 在赋权图,可能会出现负值边的情况,这样当我们去找最短路径时,可能会产生负值圈,毕竟一直走负值边可以将数值变得更短。

单源最短路径问题

  • 给定一个赋权图G=(V,E)和一个特定顶点s作为输入,找出从s到G中每一个其他顶点的最短赋权路径。

无权最短路径

  • 给定一个无权图G=(V,E)和一个起始顶点s作为输入,找出从s到G中每一个其他顶点的最短路径。

广度优先搜索算法(BFS)

概念

  • 广度优先搜索算法(BFS)用于在无权图或者边权相同的图中寻找最短路径。
  • 该方法按层处理顶点,首先从起始点出发,进行发散找到与起始点邻接的顶点a,…,并将s到这些顶点的路径距离更新,然后将该点标记成已经访问的顶点并将该点的前一个顶点记录下来(被标记的顶点我们后面遇到就认为该点已经不需要再进行处理了),然后再从顶点a,…发散,找到该顶点的邻接顶点,然后重复操作直到所有顶点都被标记完,就完成了搜索。
  • 具体代码实现,是用一个队列,在迭代开始时,队列中只含有距离为迭代距离currdist的那些顶点,然后执行操作时,将距离currdist+1的顶点的那些顶点添加到队列中,只要当前距离为currdist顶点处理完,就会处理距离为currdist+1(也就是当前顶点发散的顶点)的顶点添加到队列中。
  • 在队列中其实可以将know域也就是标记去掉,因为队列的形式已经说明执行过了,就不会在执行,因此相当于标记了。

代码:

//图的邻接表的结点信息
struct listnode{int data;bool flag;    //判断是否访问过int path;     //存储上一个顶点int dist;     //距离listnode* next;
};//图的信息
class graph{
private:listnode *an;   //邻接表形式存储图int vnum;     //图中结点数
};//s为起点,an数组的邻接表表示图
void BFS(int s){queue<int>q;q.push(s);an[s].dist=0;while (!q.empty()){int v=q.front();q.pop();an[v].flag= true;listnode* p=an[v].next;while (p!= nullptr){if(an[p->data].dist==INT_MAX){an[p->data].dist=an[v].dist+1;an[p->data].path=v;q.push(p->data);}p=p->next;}}}

Dijkstra算法

概念

  • 用于求解赋权图的最短路径(无负值边),Dijkstra算法是解决单源最短路径问题的一般方法,并且该解法是贪心算法。Dijkstra只是BFS的升级版使他能够求赋权图的最短路径,如果求无权图Dijkstra跟BFS的做法一样!
  • Dijkstra算法是分阶段的,该算法认为每一个阶段,都将该阶段当作最好的情况处理,类似于BFS算法,但是还是有不同的地方,比起BFS多出了需要进行每个阶段出现最好情况就进行更新路径。
  • 具体做法是,从图中选取起始点v,然后找出邻接点,并将当前起始点到邻接点v3,v4…的距离更新,如果是赋权图就是dv+Cv,v3(就是顶点v到v3的权),如果是无权就是dv+1,并将v标记为已知。然后选取邻接点集中的一点再做为起始点,然后重复操作,将当前顶点的前一个顶点记录。当v到某个顶点的距离在当前阶段是最小的(最好情况),那么就进行更新,如果不是就无需更新
  • 具体来说,当我们扩展一个新结点时,我们会考虑它的所有未访问过的邻接结点,并计算从起始结点经过当前结点到达邻接结点的路径长度。如果这个长度小于已知的最短路径长度(或者邻接结点的路径长度尚未初始化),我们就更新邻接结点的路径长度。这样做的目的是通过不断更新路径长度来找到起始结点到所有其他结点的最短路径。
  • 实现的时候可以使用优先队列来进行优化算法,只将顶点和其最短路径值进入队列中当队列非空,执行以下操作:u等于队顶的节点w等于队顶节点的最短路径值遍历u的所有边,如果能找到节点v最短路径值小于v的当前值,更新v,将v压入队列。结束
  • 没有用优先队列优化的Dijkstra算法的时间复杂度为O(N²),如果使用优先队列,则时间复杂度为O(nlogn),可以不用考虑已知域;

Dijkstra跟BFS区别:

  1. 处理顶点
    • BFS算法中,当一个顶点被扩展时,它的所有未访问过的邻居顶点都被添加到队列中,这样它们将按照遍历的顺序逐个被访问。
    • Dijkstra算法中,当一个顶点被扩展时,它的邻居顶点也被考虑,但是Dijkstra算法会计算扩展的顶点与其邻居之间的边的权重,并根据这个权重来更新到达邻居顶点的最短路径长度。这个更新过程使得Dijkstra算法能够处理带有非负权重的图。
  2. 选择下一个顶点
    • BFS算法中,下一个要被扩展的顶点是队列中的下一个顶点,也就是按照队列的先进先出(FIFO)原则选择。
    • Dijkstra算法中,下一个要被扩展的顶点是距离起始点路径长度最小的顶点,也就是根据已知的最短路径长度来选择。这需要使用优先队列或者最小堆来高效地选择路径长度最小的顶点。

代码:

//图的邻接表的结点信息
struct listnode{int data;int path;     //存储上一个顶点int dist;     //最短距离int weight;   //数组索引顶点跟该顶点的边的权重listnode* next;
};//图的信息
class graph{
private:listnode *an;   //邻接表形式存储图int vnum;     //图中结点数
};//v是起始点
void Dijkstra(int v){an[v].dist=0;queue<int>q;q.push(v);while (!q.empty()){int w=q.front();q.pop();listnode* p=an[w].next;while (p!= nullptr){if(an[w].dist+p->weight<an[p->data].dist){an[p->data].dist=an[w].dist+p->weight;an[p->data].path=w;q.push(p->data);}p=p->next;}}}

题目模板
有向边单源最短路径问题

#include <bits/stdc++.h>
using namespace std;const int INF=0x3f3f3f3f;const int N=10;
int n;struct edge {int v, w;
};bool vis[N+1];int dijkstra(int start, const vector<vector<edge>>& graph) {int minroad[n+1];memset(minroad,INF,sizeof minroad);minroad[start] = 0;priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;pq.push({0, start});while (!pq.empty()) {auto [d, u] = pq.top();pq.pop();if(vis[u]) continue;vis[u]=true;for (const auto& edges : graph[u]) {int v = edges.v;int w = edges.w;if (minroad[u] + w < minroad[v]) {minroad[v] = minroad[u] + w;pq.push({minroad[v], v});}}}return minroad[n]!=INF?minroad[n]:-1;
}int main() {int m, start;cin >> n >> m >> start;vector<vector<edge>> graph(n + 1);for (int i = 0; i < m; i++) {int u, v, w;cin >> u >> v >> w;graph[u].push_back({v, w});}cout<<dijkstra(start, graph)<<endl;system("pause");return 0;
}

Floyd算法

概念

  • Floyd(弗洛伊德)算法是基于动态规划思想的算法,也称插点法,是全源最短路算法(全源就代表经过一次Floyd算法,每个点到各个点的最短路径都能算出来)
  • 用于求任意一对顶点间的最短路径,此时图中的边的权值可以出现负数,但不能出现负环
  • 时间复杂度为O(n³),n为点个数

基本思想

  1. 对于从i到j的弧,进行n次试探
  2. 首先考虑i,0,j是否存在,如果存在,则比较i,j和i,0,j的路径长度,去最短者进行更新i,j的最短路径
  3. 然后再添加顶点1,依次类推。

具体过程

  1. 当一个图里有n个城市,求全源最短路径问题
  2. 定义城市k为从当前图拿出来,并重新插入图中的城市城市i城市j分别为当前源城市目的城市dist[i\][j]表示城市i到城市j的最短路径
  3. 假设当前图中没有城市k,我们将城市k重新插入到图中
  4. 我们需要判断城市i到城市j的最短路径是否要更新,则比较路径经过城市k和原来的路径长度进行比较,如果经过城市k的路径长度更短,则更新dist[i][j],因此就为dist[i][j]=min(dist[i][k]+dist[k][j],dist[i][j])
  5. 因此对这个图执行n次上述操作(也就是插点法),得出的dist二维数组就为全源的最短路径。

代码模板

//dist[n][n]用来记录图中各点到各点的最短路径
void Floyd(int **dist){for(int k=0;k<n;++k){for(int i=0;i<n;++i){for(int j=0;j<n;++j){if(dist[i][k]+dist[k][j]<dist[i][j]){dist[i][j]=dist[i][k]+dist[k][j];}}}}
}

例题部分代码

具体可看力扣1334. 阈值距离内邻居最少的城市,只包含求解全源最短路径代码

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;void Floyd(int n, vector<vector<int>>& edges){const int INF=0x3f3f3f3f;int dist[n][n];memset(dist,INF, sizeof(dist));for(int i=0;i<n;++i){dist[i][i]=0;}for(auto edge:edges){dist[edge[0]][edge[1]]=edge[2];dist[edge[1]][edge[0]]=edge[2];}//Floyd算法计算全源最短路代码for(int k=0;k<n;++k){for(int i=0;i<n;++i){for(int j=0;j<n;++j){if(dist[i][k]+dist[k][j]<dist[i][j]){dist[i][j]=dist[i][k]+dist[k][j];}}}}for(int i=0;i<n;++i){cout<<"第"<<i<<"城市到其他城市最短路径:";for(int j=0;j<n;++j)cout<<"("<<i<<","<<j<<","<<dist[i][j]<<")"<<" ";cout<<endl;}
}int main() {vector<vector<int>>edges{{0,1,2},{0,4,8},{1,2,3},{1,4,2},{2,3,1},{3,4,1}};Floyd(5,edges);system("pause");return 0;
}

尾言

完整版笔记也就是数据结构与算法专栏完整版可到我的博客进行查看,或者在github库中自取(包含源代码)

  • 博客1: codebooks.xyz
  • 博客2:moonfordream.github.io
  • github项目地址:Data-Structure-and-Algorithms

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

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

相关文章

mac安装配置cmake

本机是2015 macbook pro mid&#xff0c;已经有点老了&#xff0c;用homebrew下cmake老出问题 其实cmake官网安装也不麻烦 一、官网下载对应安装包 Download CMake 和所有dmg文件一样安装 二、改成命令行使用 一般来说 tutorial 给的都是命令行build 命令行的设置如下&am…

手机下载APP (uniapp/vue)

一、uniapp <template><view class"content"><view class"appName">{{ formData.appName }}</view><view class"appInfo">{{ formData.appInfo }}</view><image class"logo" :src"formDa…

批量修改Git历史commit信息中的username

之前很长一段时间GitHub上的提交都在使用工作账户, 导致私人仓库中的提交者比较混乱. 在StackOver里面找到了一个bash脚本可以批量修改username, 在这里记录一下. 修改的步骤一共两步: 执行修改脚本将本地修改同步到Git服务器 首先我们来看脚本: #!/bin/shgit filter-branch…

SFUZZ模糊测试平台全新升级,从标准到实践助力车企安全出海

开源网安模糊测试平台SFuzz全新升级&#xff0c;参照各国相关标准要求进行针对性建设&#xff0c;可为智能网联汽车信息安全测试提供更为强大的工具支持。SFuzz向被测系统输入大量随机数据&#xff0c;模拟各种异常情况&#xff0c;可以发现被测系统内潜在的缺陷和漏洞&#xf…

Spring中如何操作Redis

Spring毕竟是Java中的一个主流框架&#xff0c;如何在这个框架中使用Redis呢&#xff1f; 创建项目并引入相关依赖 然后进行创建。 至此就将Redis的相关依赖引入进来了。 编写Redis配置 将application.properties修改成application.yml 然后编写如下配置&#xff1a; spr…

usbserver工程师手记(二)设置定时任务

概述 部分银行ukey 长时间不使用后会导致休眠&#xff0c;出现虽然有连接&#xff0c;但是读不到证书&#xff0c;可以用定时重置端口的办法&#xff0c;调用接口 http://ip/usb_server/reset_port,参数为 {"port":"B5-1-2","vid_pid":"09…

Golang | Leetcode Golang题解之第228题汇总区间

题目&#xff1a; 题解&#xff1a; func summaryRanges(nums []int) (ans []string) {for i, n : 0, len(nums); i < n; {left : ifor i; i < n && nums[i-1]1 nums[i]; i {}s : strconv.Itoa(nums[left])if left < i-1 {s "->" strconv.It…

多个标签页中复用同一 QTableView

在 PyQt 中实现在多个标签页中复用同一个 QTableView 实例&#xff0c;复用同一个 QTableView 实例可以减少内存和资源的使用。每个 QTableView 实例都会消耗一定的内存和处理资源&#xff0c;如果每个标签页都创建一个新的实例&#xff0c;会增加系统的负担。通过复用实例&…

每天一个数据分析题(四百二十一)- 一元线性回归模型

关于一元线性回归的求解过程说法正确的是&#xff1f; A.一元线性回归只需要求解出两个参数系数即可 B.对于新来的样例&#xff0c;建立好的一元线性回归模型可以做出准确的预测 C.一元线性回归模型的基本形式是YAxe&#xff0c;其中A为系数&#xff0c;e为随机误差 D.一元线性…

日常学习-20240710

1、一次一千万条数据插入和删除案例&#xff1a; 第一次&#xff1a;插入--批量插入&#xff0c;每次插入5000条数据&#xff0c;总耗时28min,数据无异常 删除--通过sql语句一次性删除&#xff0c;总耗时1h52min;一次删除的数据过多导致mysql的备份恢复文件极其庞大&#xff0…

CentOS7 安装 git 命令

通过yum源install下载的git版本比较低&#xff0c;不推荐此方式安装。 官网下载最新版git源码&#xff1a;Git 1. 解压安装包 tar -xzvf git-2.45.2.tar.gz 2. 安装相关依赖 yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc perl-ExtUtils…

uniapp使用高德地图(公众号+h5)

选择微信小程序的话后果就是你的地图出不来&#xff0c;出来了就报key异常 下面直接放配置和代码&#xff1a; 打包后的高德uni-app,uniCloud,serverless,高德地图,申请高德地图Key,配置使用高德地图,参数说明,高德开放平台用户名,百度地图,申请百度地图Key,配置使用百度地图,…

线性代数|机器学习-P22逐步最小化一个函数

文章目录 1. 概述2. 泰勒公式3. 雅可比矩阵4. 经典牛顿法4.1 经典牛顿法理论4.2 牛顿迭代法解求方程根4.3 牛顿迭代法解求方程根 Python 5. 梯度下降和经典牛顿法5.1 线搜索方法5.2 经典牛顿法 6. 凸优化问题6.1 约束问题6.1 凸集组合 Mit麻省理工教授视频如下&#xff1a;逐步…

bert训练的一些技巧(rand() < self.skipgram_prb)

rand() < self.skip_gram_prb) 是一个条件表达式&#xff0c;用来判断是否进行skip-gram掩码操作。这种掩码操作通常用于自然语言处理中的数据增强&#xff0c;通过概率决定是否应用skip-gram掩码。下面是对这个表达式的详细解释&#xff1a; 解释 rand(): rand() 是一个随…

uniapp 初始学习1

uni-app代码基本包括js,vue,css.在app端支持原生渲染nvue&#xff0c;可编译的kotlin和swift 掌握js就可以进行不同应用的开发 页面文件遵循 Vue 单文件组件 (SFC) 规范&#xff0c;即每个页面是一个.vue文件 .vue文件是一个自定义的文件类型&#xff0c;用类HTML语法描述一…

SpringBoot使用RedisTemplate、StringRedisTemplate操作Redis

前言 RedisTemplate 是 Spring Boot 访问 Redis 的核心组件&#xff0c;底层通过 RedisConnectionFactory 对多种 Redis 驱动进行集成&#xff0c;上层通过 XXXOperations 提供丰富的 API &#xff0c;并结合 Spring4 基于泛型的 bean 注入&#xff0c;极大的提供了便利&#x…

深度学习和NLP中的注意力和记忆

深度学习和NLP中的注意力和记忆 文章目录 一、说明二、注意力解决了什么问题&#xff1f;#三、关注的代价#四、机器翻译之外的关注#五、注意力&#xff08;模糊&#xff09;记忆&#xff1f;# 一、说明 深度学习的最新趋势是注意力机制。在一次采访中&#xff0c;现任 OpenAI 研…

使用 python 构建企业级高可用海量爬虫调度系统

一、引言 在大数据时代&#xff0c;信息的获取与分析成为了企业决策的重要依据。对于营销行业而言&#xff0c;实时抓取和分析竞争对手动态、市场趋势以及用户反馈等数据&#xff0c;是制定有效策略的关键。然而&#xff0c;构建一个高可用的、能够处理海量数据的爬虫调度系统…

K8S中部署 Nacos 集群

1. 准备 GitK8Skubectlhelm 咱也没想到 K8S 部署系列能搞这么多次&#xff0c;我一个开发天天干运维的活&#xff0c;前端后端运维测试工程师实至名归。 2. 方案选择 https://github.com/nacos-group/nacos-k8s 我替你们看了一下&#xff0c;有好几种方式能部署&#xff…

华为机考真题 -- 求字符串中所有整数

题目描述&#xff1a; 输入字符串s&#xff0c;输出s中包含所有整数的最小和。 说明&#xff1a;字符串s&#xff0c;只包含 a-z A-Z &#xff1b; 合法的整数包括&#xff1a; 1&#xff09; 正整数 一个或者多个0-9组成&#xff0c;如 0 2 3 002 102 2&#xff09;负整数…