目录

一.朴素Dijkstra算法

二.堆优化的Dijkstra

三.bellman_ford算法

四.spfa算法

五.floyd算法


使用区别:

所有边权都是正数的单源最短路:朴素Dijkstra算法,堆优化的Dijstra算法。

存在负权边的单源最短路:Bellman-ford,spfa。

多源汇最短路:floyd算法。​​​​​​​

总结:

朴素Dijkstra算法: 适用于稠密图,适合邻接矩阵存储,时间复杂是 O(n^2+m), n表示点数,m  表示边数。

堆优化的Dijkstra算法:适用于稀疏图,适合邻接表存储,时间复杂度是O(mlogn)

Bell-Ford算法:  适用于处理可能存在负环的有限路线单源最短路问题,时间复杂度是O(nm),一般不能有负权回路。

spfa算法:时间复杂度是O(m)最坏O(nm)。

floyd算法:时间复杂度O(n^3)。

一.朴素Dijkstra算法

简介:迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

算法思路:

按路径长度递增次序产生算法:

把顶点集合V分成两组:

(1)S:已求出的顶点的集合(初始时只含有源点V0)

(2)V-S=T:尚未确定的顶点集合

将T中顶点按递增的次序加入到S中,保证:

(1)从源点V0到S中其他各顶点的长度都不大于从V0到T中任何顶点的最短路径长度

(2)每个顶点对应一个距离值

S中顶点:从V0到此顶点的长度

T中顶点:从V0到此顶点的只包括S中顶点作中间顶点的最短路径长度

依据:可以证明V0到T中顶点Vk的,或是从V0到Vk的直接路径的权值;或是从V0经S中顶点到Vk的路径权值之和。

算法步骤如下:

G={V,E}

1. 初始时令 S={V0},T=V-S={其余顶点},T中顶点对应的距离值

若存在,d(V0,Vi)为弧上的权值

若不存在,d(V0,Vi)为∞

2. 从T中选取一个与S中顶点有关联边且权值最小的顶点W,加入到S中

3. 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值

重复上述步骤2、3,直到S [1]  中包含所有顶点,即W=Vi为止

简单地说就是先找出一个距离起点最近的,然后再由这个距离起点最近的点更新别的点

题目:https://www.acwing.com/problem/content/851/

 代码如下:

#include<bits/stdc++.h>
using namespace std;
//dijkstra算法适用于正权边稠密图
const int N = 510;
int n,m;
int dist[N],g[N][N];//存放距离和图
bool st[N];//存放状态 int dijkstra()
{memset(dist,0x3f,sizeof dist);dist[1] = 0;//遍历n次,将n个点的最值都找出for(int i=0;i<n-1;i++){int t = -1;//点 //从不确定的点中找出距离一号点最短的点for(int j=1;j<=n;j++){if(!st[j]&&(t==-1||dist[t]>dist[j])) t = j;//依次比较距离1号点最近的点赋给t } //根据t更新t的出边for(int j = 1;j<=n;j++){dist[j] = min(dist[j],dist[t]+g[t][j]);} //表示这个点已经确定了 st[t] = true;} //如果没有边就是无穷 if(dist[n] == 0x3f3f3f3f) return -1;return dist[n];
}int main()
{cin>>n>>m;memset(g,0x3f,sizeof g);while(m--){int a,b,c;cin>>a>>b>>c;g[a][b] = min(g[a][b],c);//因为有重边,取一条最短的边 }int t = dijkstra();cout<<t<<" ";return 0;
}

二.堆优化的Dijkstra

简介:在确定一个还未确定最短距离的点中距离源点最近距离的点时 朴素Dijkstra 的方法是遍历所有的点通过比较找出最近的点,在这个地方可以使用 优先队列(小根堆) 来进行优化,在所有的点中取出距离最小的点。

算法思路:

首先,将 优先队列 定义成 小根堆,将源点的距离初始化为 0 加入到优先队列中。

然后,从这个点开始扩展。先将队列中的队头元素 ver 保存到一个临时变量中,并将队头元素出队,然后遍历这个点的所有出边所到达的点 j,更新所有到达的点距离源点最近的距离。

如果源点直接到 j 点的距离比源点先到 ver 点再从 ver 点到 j 点的距离大,那么就更新 dist[j],使 dist[j] 到源点的距离最短,并将该点到源点的距离以及该点的编号作为一个 pair 加入到优先队列中,然后将其标记,表示该点已经确定最短距离。因为是小根堆,所以会根据距离进行排序,距离最短的点总是位于队头。一直扩展下去,直到队列为空。

因为有 重边 的缘故,所以该点可能会有冗余数据,即如果在扩展的时候,第一次遍历到的点是 2 号点,距离 源点 的距离为 10,此时 dist[2] = 0x3f3f3f3f > dist[1] + distance[1 -> 2] = 0 + 10 = 10 所以 dist[2] 会被更新为 10,此时会将 {10, 2} 入队。但是很不巧从 源点 到 2 号点有一个距离为 6 的重边,当遍历到这个重边时,由于 dist[2] = 10 > dist[1] + distance[1 -> 2] = 0 + 6 = 6,所以 {6, 2} 也入队了,入队之后由于是 小根堆 所以 {6, 2} 会排在 {10, 2} 前面,所以 {6, 2} 会先出队,出队之后会被标记。所以当下一次再遇到已经被标记的 2 号点时,直接 continue 忽略掉冗余数据继续扩展下一个点即可。

题目:https://www.acwing.com/problem/content/852/

 代码如下:

#include<bits/stdc++.h>
using namespace std;const int N = 1e6+10;
int h[N],e[N],ne[N],w[N],idx;//稀疏图采用邻接表存储
typedef pair<int,int> p;//存结点距离和编号
int dist[N];
int n,m;
bool st[N];
//使用邻接表存储
void add(int a,int b,int c)
{e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a]=idx++;
}int dijkstra()
{priority_queue<p,vector<p>,greater<p> >  q;memset(dist,0x3f,sizeof dist);q.push({0,1});dist[1] = 0;while(!q.empty()){p t = q.top();q.pop();int ver = t.second,dis = t.first;//如果此节点已经确定最小值就continue if(st[ver]) continue;st[ver] = true;//else就拓展此节点,根据t更新t的拓展节点for(int i = h[ver];i!=-1;i=ne[i]){int j = e[i];if(dist[j]>dist[ver]+w[i]) {dist[j] = dist[ver]+w[i];q.push({dist[j],j});}  } }if(dist[n] == 0x3f3f3f3f) return -1;return dist[n];
}int main()
{cin>>n>>m;memset(h,-1,sizeof h);while(m--){int a,b,c;cin>>a>>b>>c;add(a,b,c);}cout<<dijkstra()<<" "; return 0;
} 

三.bellman_ford算法

简介:贝尔曼-福特算法(Bellman-Ford)是由理查德·贝尔曼和莱斯特·福特创立的,求解单源最短路径问题的一种算法。它的原理是对图进行V-1次松弛操作,得到所有可能的最短路径。其优于Dijkstra算法的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高,最坏O(nm).

Bellman-Ford算法是一种处理存在负权边的单元最短路问题的算法。解决了Dijkstra无法求的存在负权边的问题。 虽然其算法效率不高,但是也有其特别的用处。其实现方式是通过m次迭代求出从源点到终点不超过m条边构成的最短路的路径。一般情况下要求途中不存在负环。但是在边数有限制的情况下允许存在负环。因此Bellman-Ford算法是可以用来判断负环的。很重要的一点是本算法每次迭代都是在上一次的基础上进行的,因此我们在代码实现时要注意保留上一次的结果,在上一次的基础上算,故需要将上次的数组copy一份。

模板题目:https://www.acwing.com/problem/content/855/

​​​​​​​

代码如下:

#include<bits/stdc++.h>using namespace std;const int N = 510,M = 10010;
int dist[N],backup[N];
int n,m,k;
struct Edge{int a,b,c;
}edges[M];void bellman_ford()
{memset(dist,0x3f,sizeof dist);dist[1] = 0;//k表示从i到j经过k条边for(int i=0;i<k;i++){memcpy(backup,dist,sizeof dist);for(int j=0;j<m;j++){int a = edges[j].a , b = edges[j].b , c = edges[j].c;//当dist[b]有值时,表示以b为尾的这条边更新完了dist[b] = min(dist[b],backup[a]+c);}}}int main()
{cin>>n>>m>>k;for(int i=0;i<m;i++){int a,b,c;cin>>a>>b>>c;edges[i] = {a,b,c};}bellman_ford();if (dist[n] > 0x3f3f3f3f / 2) puts("impossible");else printf("%d\n", dist[n]);return 0;
}

四.spfa算法

简介: SPFA 算法是Bellman-Ford算法的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。SPFA 最坏情况下复杂度和朴素Bellman-Ford相同,为O(nm)

优化思路:

Bellman-Ford算法在每次更新时,遍历了所有的边,而也如前面看到的,每次遍历未必会真的更新最短距离。SPFA算法就是对该步骤的优化。每次只有当前的起点到源点的距离变小了,该点连通的其他点才会变小。
只要一个结点的边变小了,我们就把他放进队列(其他的也行)中,只要队列中还有值,我们就继续更新,更新到的点也加入队列,如此往复,直到标记全部的点。

题目:https://www.acwing.com/problem/content/853/

 代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N = 100010;int h[N],e[N],w[N],ne[N],idx;
int dist[N];
bool st[N];
int n,m;void add(int a,int b,int c)
{e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}int spfa()
{queue<int> q;memset(dist,0x3f,sizeof dist);dist[1] = 0;q.push(1);st[1] = true;while(!q.empty()){int t = q.front();q.pop();st[t] = false;for(int i=h[t];i!=-1;i=ne[i]){int j = e[i];if(dist[j]>dist[t]+w[i]){dist[j] = dist[t]+w[i];if(!st[j]){q.push(j);st[j] = true;}}}}return dist[n];
}int main()
{cin>>n>>m;memset(h,-1,sizeof h);while(m--){int a,b,c;cin>>a>>b>>c;add(a,b,c);}int t = spfa();if(t==0x3f3f3f3f) cout<<"impossible"<<endl;else cout<<t<<endl;return 0;
} 

五.floyd算法

简介:弗洛伊德算法是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权(但不可存在负权回路)的最短路径问题,同时也被用于计算有向图的传递闭包,Floyd 算法是一个基于贪心、动态规划求一个图中 所有点到所有点 最短路径的算法。

算法思路:

以每个点为「中转站」,刷新所有「入度」和「出度」的距离。每次从「未求出最短路径」的点中 取出 最短路径的点,并通过这个点为「中转站」刷新剩下「未求出最短路径」的距离。简单地说就是dist[i,j]表示从i到j经过k这个点的最短距离是多少。

题目:https://www.acwing.com/problem/content/853/

代码如下:

#include<bits/stdc++.h>
using namespace std;const int N = 210,M = 20010;
int dist[N][M];
int n,m,k;void floyd()
{
//以k这个点作为中转点更新从i到j的距离for(int k=1;k<=n;k++){for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)//比较从i直接到j的距离和从i到j以k为中转的距离最小值dist[i][j] = min(dist[i][j],dist[i][k]+dist[k][j]);}}
}int main()
{cin>>n>>m>>k;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){if(i==j) dist[i][j]=0;else dist[i][j] = 0x3f3f3f3f;}while(m--){int x,y,z;cin>>x>>y>>z;dist[x][y] = min(dist[x][y],z);}floyd();while(k--){int x,y;cin>>x>>y;if(dist[x][y]>0x3f3f3f3f/2) cout<<"impossible"<<endl;else cout<<dist[x][y]<<endl;}return 0;
}

详解五种最短路径算法及其区别(c++)相关推荐

  1. 五种最短路径算法的总结(待更新)

    最短路径算法: 1:Dijkstra     2:Floyd     3:Bellman-Ford     4:SPFA     5:A* 这五种最短路径算法初学的时候非常容易混淆,因为他们的松弛方法 ...

  2. 图的五种最短路径算法

    本文总结了图的几种最短路径算法的实现:深度或广度优先搜索算法,费罗伊德算法,迪杰斯特拉算法,Bellman-Ford 算法. 1)深度或广度优先搜索算法(解决单源最短路径) 从起点开始访问所有深度遍历 ...

  3. Python 实操案例:一文详解10种聚类算法

    聚类或聚类分析是无监督学习问题.它通常被用作数据分析技术,用于发现数据中的有趣模式,例如基于其行为的客户群.有许多聚类算法可供选择,对于所有情况,没有单一的最佳聚类算法.相反,最好探索一系列聚类算法以 ...

  4. Windows下的PHP开发环境搭建——PHP线程安全与非线程安全、Apache版本选择,及详解五种...

    2019独角兽企业重金招聘Python工程师标准>>> 今天为在Windows下建立PHP开发环境,在考虑下载何种PHP版本时,遭遇一些让我困惑的情况,为了解决这些困惑,不出意料地牵 ...

  5. html5读取运动传感器,详解五种类型的运动传感器

    运动传感器,顾名思义,指检测物体或人运动的装置,包括重力,线性加速度,旋转矢量.振动频率等.运动传感器可用于监视设备移动,如倾斜,摇晃,旋转或摆动. 其中,旋转矢量传感器和重力传感器是用于运动检测和监 ...

  6. 【双子真经】详解五种不同周期的双子——双子巨蟹座(6/19-6/24)

    双子巨蟹座(6/19-6/24):神奇的交界 黄道宫位置:约在双子座27度-巨蟹座4度 季节:春末初夏(夏至) 元素:风.水 主宰行星:水星.月亮 象征符号:双胞胎.螃蟹 理解事物的方式:思考.感受 ...

  7. 图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)

    图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS) 阅读本文前,请确保你已经掌握了递归.栈和队列的基本知识,如想掌握搜索的代码实现,请确保你能够用代码实现栈和队列的基本操作. 深度优先遍 ...

  8. 详解23种设计模式(基于Java)—— 结构型模式(三 / 五)

    目录 3.结构型模式(7种) 3.1.代理模式 3.1.1.概述 3.1.2.结构 3.1.3.静态代理 3.1.4.JDK动态代理 3.1.5.CGLIB动态代理 3.1.6.三种代理的对比 3.1 ...

  9. python算法详解 张玲玲_Python算法详解

    目 录 第 1章 算法概述 1 1.1 算法的基础 2 1.1.1 算法的特征 2 1.1.2 何为算法 2 1.2 计算机中的算法 3 1.2.1 认识计算机中的算法 3 1.2.2 为什么说算法是 ...

最新文章

  1. 【怎样写代码】小技巧 -- 关于引用类型的两种转换方式
  2. R语言ggplot2可视化散点图(scatter plot)、并在可视化图像的顶部和右边添加边缘直方图(Marginal Histogram)、使用geom_smooth函数基于lm方法拟合数据点之间
  3. java ajax上传文件图片以json形式返回
  4. MySQL Replication--复制异常1
  5. 中国软件开发工程师之痛
  6. 梯度下降和随机梯度下降_梯度下降和链链接系统
  7. java 数组 树_java使用顺序数组实现二叉树
  8. Java的几何布朗运动
  9. 来了就不会空着手回去.
  10. 深度学习在美团推荐平台排序中的运用
  11. x264中I,P,B帧和PTS,DTS的关系
  12. 原 jQuery基础修炼圣典—DOM篇
  13. SpringCloud工作笔记059---Jquery消息提示插件toastr使用详解
  14. SpringBoot实战(十六):集成Skywalking调用链监控系统
  15. php生成迷宫图片,PHP实现基于回溯法求解迷宫问题的方法详解
  16. 站长屋VPS独家erphpdown10.2会员中心美化页面wordpress通用插件
  17. 鸟哥私房菜重温笔记4
  18. 已知三角函数值用计算机如何求角度,【已知三角函数值求角度】第一册已知三角函数值求角...
  19. android百度地图多路线规划,android百度地图开发之步行路线规划
  20. 记录一次opper R9s Plus 从发现循环重启到解决的过程

热门文章

  1. SpringBoot全局异常处理
  2. 解决Idea使用git时commit特别慢的问题
  3. Android 仿钉钉、微信 群聊组合头像,大厂面试题汇总
  4. 诚之和:抛售11亿美元股票,马斯克亲自砸盘特斯拉
  5. 报错JDBC Connection [com.mysql.jdbc.JDBC4Connection@184c65da] will not be managed by Spring
  6. Webmail攻防实战
  7. 【算法leetcode】1557. 可以到达所有点的最少点数目(rust和go我都要)
  8. OpenGL入门学习[三]
  9. 前谷歌员工推Cuil 获3300万风投对战老东家
  10. 研究黑苹果系统——拯救者Y7000P-10875H