最短路径(dijstra算法,链式前向星,堆优化)

2024-02-16 18:12

本文主要是介绍最短路径(dijstra算法,链式前向星,堆优化),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【模板】单源最短路径(弱化版)

对于这题我们使用邻接矩阵的话会导致弓箭复杂度会大大提升,所以我们就需要学习一种新的数据结构,名叫链式前向星,在链式前向星中,我们需要定义一个结构体数组,其中有成员to,w,next;

struct EGDE
{int to;//终点,仅仅是指两个点之间int w;//权值,路径长度int next;//指向前一个点
}edge[maxn];
int first[maxn];

接下来构造存放边的函数,姑且叫做cinn函数

请大家将first数组想象成一个一个鞭子的把,next想象成鞭子的每一节,那么这个数组就可以按照鞭子的样子将所有边的信息存起来了。其实这个就是邻接表的升级版

void cinn(int u,int w,int v)//w代表着权值,u代表着first数组的下标,v代表着终点
{cnt++;//代表着下标,这个下标指的是边的下标edge[cnt].to=v;edge[cnt].w=w;edge[cnt].next=first[u];first[u]=cnt;	
}

接下来就是在主函数的输入的问题

	cin>>n>>m;//n代表着节点数,m代表着边数for(int i=1;i<=m;i++){int u,v,w;cin>>u>>v>>w;//u代表着fisrt数组的下标,其中存放的值就是相当于这个鞭子的把,同时也是代表着起点cinn(u,v,w);//v代表着终点,w代表着权值cinn(v,u,w);//如果是有向边则只需要一个这样的函数,如果是无向边那么就需要写两个这样的函数}

如何遍历这个鞭子呢,我们设计一个vis函数用于遍历,优势链式储存所以就需要将把拿到手,那么这个把就是出差呢在first数组里面,我们只需要将我们需要查找的链子的下标输进去即可

void vis(u)
{for(int i=first[u];i!=0;i=edge[i].next)//i的变化里面的i就是相当于first[u]{printf("%d--%d:%d\n",u,edge[i].to,edge[i].w);}
}

前向链式星结构的最终代码

#include<iostream>
#define N 10000
using namespace std;
int cnt = 0;
struct EGDE
{int to;//终点,仅仅是指两个点之间int w;//权值,路径长度int next;//指向前一个点
}edge[N];
int first[N];
int n, m;
void cinn(int u, int v, int w)//w代表着权值,u代表着first数组的下标,v代表着终点
{cnt++;//代表着下标,这个下标指的是边的下标edge[cnt].to = v;edge[cnt].w = w;edge[cnt].next = first[u];first[u] = cnt;
}
void vis(int u)
{for (int i = first[u]; i != 0; i = edge[i].next)//i的变化里面的i就是相当于first[u]{printf("%d--%d:%d\n", u, edge[i].to, edge[i].w);}
}
int main()
{cin >> n >> m;//n代表着节点数,m代表着边数int s;cin>>s;for (int i = 1; i <= m; i++){int u, v, w;cin >> u >> v >> w;//u代表着fisrt数组的下标,其中存放的值就是相当于这个鞭子的把,同时也是代表着起点cinn(u, v, w);//v代表着终点,w代表着权值}vis(s);return 0;
}

最后我们按照dijkstra算法的思路走一遍代码就可以出来了

#include<iostream>
#define N 1000000
using namespace std;
int cnt = 0;
long long ans[N];
bool book[N];
int first[100000];
int n,m,s;
struct EGDE
{int to;//终点,仅仅是指两个点之间int w;//权值,路径长度int next;//指向前一个点
}edge[N];
void cinm(int u, int v, int w)//w代表着权值,u代表着first数组的下标,v代表着终点
{//代表着下标,这个下标指的是边的下标edge[++cnt].to = v;edge[cnt].w = w;edge[cnt].next = first[u];first[u] = cnt;
}
int main()
{cin >> n >> m>>s;//n代表着节点数,m代表着边数for(int i=1;i<=m;i++)//将每一条边都设置为是最大值{ans[i]=2147483647;}ans[s]=0;//由于是从s开始所以要将着一条边设置为0方便结束for (int i = 1; i <= m; i++){int u, v, w;cin >> u >> v >> w;//u代表着fisrt数组的下标,其中存放的值就是相当于这个鞭子的把,同时也是代表着起点cinm(u, v, w);//v代表着终点,w代表着权值}int tmp=s;while(book[tmp]==0){long long minn=2147483647;book[tmp]=1;//将已经收录过的边进行标记//Dijkstra算法的核心部分for(int i=first[tmp];i!=0;i=edge[i].next)//这个就相当于遍历已经理解起来的点{if(!book[edge[i].to]&&ans[edge[i].to]>ans[tmp]+edge[i].w)//如果没有被标记且直接的两点距离大于通过某点的距离{ans[edge[i].to]=ans[tmp]+edge[i].w;//ans数组存放的是s点到ans下标编号的这个点的最短距离,其实这里优点像动态规划}}for(int i=1;i<=n;i++)//重新遍历全部点,找到没有被标记且最小的边{if(book[i]==0&&ans[i]<minn){minn=ans[i];//这个是为找到目前情况的最小值(每次寻找都有可能找到不同的最小边)tmp=i;//这个算法下一次就从这个最小边开始(由于这个是最小便已经确定,所以可以依赖这个节点进行下移的寻找)}}}for(int i=1;i<=n;i++){cout<<ans[i]<<" ";}return 0;
}

dijkstra算法的核心两步:第一找出里目的地的最近的点(没有被收录的点之中),并将该点收录,下一次循环就从这个点开始。

第二判断这个点的距离是比从前面那个点加上前面那个点到这个点的距离谁大谁小,将小的距离重新赋给该值(动态规划的思路)。

【模板】单源最短路径(标准版)

这一题还得是用堆优化

1.这里要用到一个优先队列(stl),其本质就是用一个数组模拟的一个完全二叉树。
2.功能:拿出优先级最大的元素,这个优先级可以自己定义。
3.这个包括在头文件#include<queue>之中。
4.定义方式:priority_queue<int> que   尖括号说明里面存放的数是整型(这样定义就是大顶堆 值越大优先级越高)
5.关于优先队列的几种操作:1.que.size()  得到这个队列的元素数量
                                              2.que.push(x)  插入
                                              3.que.pop()  删除优先级最高的元素(弹出堆顶元素)
                                              4.que.top()访问优先级最高的元素(访问堆顶元素)
                                              5.que.empty()判断堆是否为空
插入删除的时间复杂空间度都是为对数级,访问堆顶元素的时间复杂度为常数级别。

接下俩是堆优先队列的一些基础操作
 

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int main()
{priority_queue<int> que;que.push(7);que.push(1);que.push(12);printf("nmber:");cout<<que.size()<<endl;while(!que.empty()){cout<<que.top()<<endl;que.pop();}cout<<endl;return 0;
}

输出数据如下(这样就可以使用堆排序)

在优先队列中其实有三个参量,第一是选择的类型,第二就是我们可以选择的容器,我们课以放一个vector<int>来表示一维数组,第三个参数就是我们的自定义如less<int>就表示大顶,greater<int>就表示小顶对(我没想到这个竟然是反着来的)

priority_queue<int,vector<int>,greater<int>>//从小到大

 对于我们自定义

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
struct node
{int x,y;bool operator< (const node &b) const{//运算符重新定义,注意这个运算符只能定义小于号return this->x>b.x;//从大到小}
};int main()
{priority_queue<node> que;que.push((node){5,2});que.push((node){2,4});while(!que.empty()){cout<<que.top().x<<endl;que.pop();}return 0;
}

输出结果:

在我们用基础的模板时,都会用到for循环来找到最小值(也就是打擂台的方法),但是这样就会导致时间超限,而堆每次的堆顶都是小顶堆,这样就免去了找到最小值的步骤从而减少对时间的开销。

那么有关最短路径问题我们以后就需要知道以下三点:1.对于最短路径问题我们需要使用dijstra算法    2.在dijstra算法中我们需要用到小顶堆来存放每一次的最小值。   3.我们需要使用链式前向星来存放边的信息。
  对于dijstra算法核心步骤:1.遍历与目标点相连的所有点,将其举止都更新。2.找出最小值并将其标记为以收入状态,直到所有点都已经全部标记。

代码如下:

#include<iostream>
#include<queue>
using namespace std;
const int maxN = 100010;
const int maxM = 500010;
int n, m, s, cnt;
struct node
{int id;int dis;bool operator< (const node& x) const {return x.dis < dis;}
};
priority_queue<node> q;
struct EDGE
{int to;int w;int next;
}edge[maxM];
int head[maxM];
bool vis[maxM];
int ans[maxM];
void add(int u, int v, int w)
{cnt++;edge[cnt].to = v;edge[cnt].w = w;edge[cnt].next = head[u];head[u] = cnt;
}
int main()
{cin >> n >> m >> s;for (int i = 1; i <= m; i++){int u, v, w;cin >> u >> v >> w;add(u, v, w);}for (int i = 1; i <= maxM; i++){ans[i] = 0x7fffffff;}ans[s]=0;q.push(node{s,0});while(!q.empty()){node tmp=q.top();q.pop();int k=tmp.id;if(vis[k])continue;vis[k]=true;for(int i=head[k];i!=0;i=edge[i].next){int to=edge[i].to;if(!vis[to]&&ans[to]>ans[k]+edge[i].w){ans[to]=ans[k]+edge[i].w;q.push(node{to,ans[to]});}}}for (int i = 1; i <= n; i++){cout << ans[i] << " ";}return 0;
}

这篇关于最短路径(dijstra算法,链式前向星,堆优化)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/715304

相关文章

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

VSCode设置python SDK路径的实现步骤

《VSCode设置pythonSDK路径的实现步骤》本文主要介绍了VSCode设置pythonSDK路径的实现步骤,包括命令面板切换、settings.json配置、环境变量及虚拟环境处理,具有一定... 目录一、通过命令面板快速切换(推荐方法)二、通过 settings.json 配置(项目级/全局)三、

使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)

《使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)》字体设计和矢量图形处理是编程中一个有趣且实用的领域,通过Python的matplotlib库,我们可以轻松将字体轮廓... 目录背景知识字体轮廓的表示实现步骤1. 安装依赖库2. 准备数据3. 解析路径指令4. 绘制图形关键

如何更改pycharm缓存路径和虚拟内存分页文件位置(c盘爆红)

《如何更改pycharm缓存路径和虚拟内存分页文件位置(c盘爆红)》:本文主要介绍如何更改pycharm缓存路径和虚拟内存分页文件位置(c盘爆红)问题,具有很好的参考价值,希望对大家有所帮助,如有... 目录先在你打算存放的地方建四个文件夹更改这四个路径就可以修改默认虚拟内存分页js文件的位置接下来从高级-

一文详解如何查看本地MySQL的安装路径

《一文详解如何查看本地MySQL的安装路径》本地安装MySQL对于初学者或者开发人员来说是一项基础技能,但在安装过程中可能会遇到各种问题,:本文主要介绍如何查看本地MySQL安装路径的相关资料,需... 目录1. 如何查看本地mysql的安装路径1.1. 方法1:通过查询本地服务1.2. 方法2:通过MyS

SpringBoot中HTTP连接池的配置与优化

《SpringBoot中HTTP连接池的配置与优化》这篇文章主要为大家详细介绍了SpringBoot中HTTP连接池的配置与优化的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、HTTP连接池的核心价值二、Spring Boot集成方案方案1:Apache HttpCl

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

PyTorch高级特性与性能优化方式

《PyTorch高级特性与性能优化方式》:本文主要介绍PyTorch高级特性与性能优化方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、自动化机制1.自动微分机制2.动态计算图二、性能优化1.内存管理2.GPU加速3.多GPU训练三、分布式训练1.分布式数据

MySQL中like模糊查询的优化方案

《MySQL中like模糊查询的优化方案》在MySQL中,like模糊查询是一种常用的查询方式,但在某些情况下可能会导致性能问题,本文将介绍八种优化MySQL中like模糊查询的方法,需要的朋友可以参... 目录1. 避免以通配符开头的查询2. 使用全文索引(Full-text Index)3. 使用前缀索

C#实现高性能Excel百万数据导出优化实战指南

《C#实现高性能Excel百万数据导出优化实战指南》在日常工作中,Excel数据导出是一个常见的需求,然而,当数据量较大时,性能和内存问题往往会成为限制导出效率的瓶颈,下面我们看看C#如何结合EPPl... 目录一、技术方案核心对比二、各方案选型建议三、性能对比数据四、核心代码实现1. MiniExcel