欢迎来真孝善网,为您提供真孝善正能量书籍故事!

图论中最短路算法性能对比:Floyd、Dijkstra、SPFA、Bellman算法详解

时间:11-20 神话故事 提交错误

1. Floyd

基于动态规划

复杂度O(n^3)

找到任意两点之间的最短路径

放松通过每个点的所有其他路径

递归公式map[i][j]=min(map[i][j],map[i][k]+map[k][j])

关键代码

for(int k=1; k=n; k++)

for(int i=1; i=n; i++)

for(int j=1; j=n; j++)

地图[i][j]=min(地图[i][j], 地图[i][k] + 地图[k][j]);

2. Dijkstra

单源最短路径

基于贪婪(当路径值没有负权重时)

复杂度O(N^2) (邻接矩阵) 邻接矩阵

复杂度O((M+N)logN) (邻接表) 邻接表

当图中路径很少(m n^2)(稀疏图)时,最好使用邻接表

在dis[]中寻找最小值时,如果使用C++ STL中的优先级队列priority_queue,可以对常量进行优化。

本文主要研究基于C语言的最短路径算法,不占用太多篇幅实现二叉堆排序。

想法: 找到从A 点到所有其他点的最短路径。重复下面的步骤:找到距离A最近的B点,通过B点放松其他点(A点和B点之间的当前距离一定是最短的,因为无法通过其他点放松)(无负权重)。

邻接表

邻接表的存储与链表类似,这里是通过结构体来实现的。假设路径数为m1000,节点数为n300。

typedef 结构节点{

int u、v、w; //使用该结构体来存储读取路径

}节点; //u 起点v 终点w 路径权重

节点道路[1000];再定义两个数组来模拟链表

first[]的键名代表节点,键值代表路径号。

next[]的键名代表路径号,键值代表路径号。

它们通常被定义为初始值为0 的全局数组。

int first[300];//first[u]存储从节点u开始的第一条路径(读取时反向存储)

//所以数组空间应该大于等于节点数n

int next[1000];//next[i]存储路径i的下一条路径。这两条路径的起点相同

//所以数组空间应该大于等于边数m。读取路径时如何存储

cin 路[i].u 路[i].v 路[i].w; //读取第i条路径的起点、终点、权重

下一个[i]=第一个[路[i].u];

第一[路[i].u]=i;这是反向存储,最后一个读边沿被视为第一个,在first[]中。

邻接表存储有点复杂。让我尝试解释一下。

读取第i条边时的处理方法:

first[road[i].u] 存储从节点road[i].u 开始的上一条路径的路径号(可以记为i")(如果现在读取的边以开头,则该节点是第一个起点路径,所以由于该数组是全局数组,所以first[road[i].u]的值为0,即i"==0)。现在将这条路径的编号赋给next[i],表示路径i的下一条路径是i",然后将i赋给first[road[i].u]。

邻接表的遍历

当要遍历从节点u开始的路径时,从first[u]开始,first[u]存储第一条边,然后不断获取next[边数]

int 头=第一个[u];

而(头!=0)

{

--- 在此边缘上执行操作---

头=下一个[头];

}

Dijkstra(邻接表)核心代码

for(int i=1; inw + dis[tar])

dis[nv]=nw + dis[tar];

头=最后[头];

}

}

别的

休息;

}两种方法的核心代码比较相似,变量代表的含义也基本相同。

Dijkstra(邻接矩阵)核心代码

for(int i=1; imap[1][tar] + map[tar][j])

{

地图[1][j]=地图[1][tar] + 地图[tar][j];

}

}

}

}

3. Bellman-Ford

这个算法的思路和代码量都比较短。

可以看作是在牺牲Dijkstra部分复杂度的基础上能够判断和处理负权值以及判断负权值循环。

对于n个点、m条边的有向图,使用数组dis[]保存源点到每个点的最短距离。可以遍历边n-1次,当满足dis[v] dis[u] + w 时,对其进行松弛更新操作,重复n-1次即可得到答案。如果n-1次后还能继续更新,则可以判断图中出现了负权循环。

for(int i=1; idis[路[j].u] + 路[j].w)

{

检查=1;

dis[路[j].v]=dis[路[j].u] + 路[j].w;

}

}

如果(!检查)

休息;

}

4.SPFA

SPFA是在Bellman-Ford的基础上改进的

Bellman-Ford算法中,一方面不需要在每次循环中判断所有边的松弛情况,只需要对上一次松弛操作成功松弛的节点所延伸的边进行再次判断。

因此,我们添加一个队列,让成功放松的节点进入队列。 (类似于广搜的思想。不过,在n个节点的有向图中,对于任意节点i,即使该节点在n-1次操作之前松弛成功,也不一定是最短路径,可能因此,一个节点可能会多次进入队列,这与普通的BFS不同。

由于操作是针对每个节点的,因此在稀疏图中使用邻接表会更方便(这也是我们大多数人遇到的)。

SPFA代码(放上全文,一方面SPFA是应用得最多的单源最短路算法,另一方面之前写的时候遇到了点小bug,调试了好久, 长个记性)

#include#includeusing命名空间std;

整数n,m; //n=100,m=1000

typedef 结构节点{

int u、v、w;

}节点;

节点道路[1000];

intfirst_road[100],next_road[1000]; //邻接表

int 队列[1000]; //实现队列

整数倍[100]; //每个点进入队列的次数

布尔书[1000];

整数dis[100];

布尔标志; //判断负循环

int main()

{

cin n m;

for(int i=1; i=m; i++)

{

cin 路[i].u 路[i].v 路[i].w;

next_road[i]=first_road[road[i].u];

第一路[路[i].u]=i;

}

memset(dis,0x3f, sizeof(dis));

dis[1]=0;

int 头=0,尾=1;

队列[头]=1;

书[1]=1;

次[1]++;

while(头尾)

{

int now=队列[头]; //节点

int bian=first_road[现在]; //遍历节点的路径

同时(边)

{

if(dis[路[bian].v] dis[路[bian].u] + 路[bian].w)

{

dis[路[bian].v]=dis[路[bian].u] + 路[bian].w;

int next_node=路[bian].v;

if(书[下一个节点]==0)

{

if(times[下一个节点]=n)

{

标志=1;

休息;

}

书[下一个节点]=1;

队列[tail++]=next_node;

次[下一个节点]++;

}

}

bian=next_road[bian];

}

书[队列[头]]=0;

头++;

如果(标志)

休息;

}

如果(标志)

cout "存在负循环";

别的

for(int i=1; i=n; i++)

cout dis[i] " ";

计算结束;

返回0;

}

负环判断

Bellmam-Ford 和SPFA 算法都可以确定图的负循环。

对于Bellman-Ford:第n次松弛操作也能成功更新dis[]的值,则出现负循环。

对于SPFA:如果一个点进入队列n次,则存在负循环

结论

算法简述时间复杂度负权重和负权重循环Floyd 动态规划O(N^3) 可以处理负权重,但无法判断负权重循环Dijkstra 是贪心的,点处理呢O((M+N) logN) Bellman-Ford 边缘处理O(MN) 都无法处理负权重并判断负权重循环。 SPFA优化的BellmanO(MN)可以处理负权重并判断负权重循环

遇到的一些问题:

1. memset的原理

memset是为char[]设计的,处理字节,即memset(a,0xff,sizeof(a)),将a的每个字节变成ff。

当我们对int[]使用memset时,int 4个字节的每个字节都会是ff。

使用建议:

用户评论

tina

最近在学习图论算法啊,这几个最短路算法还是很有意思的。

    有18位网友表示赞同!

(り。薆情海

Floyd算法是万能的吗?哪些情况下更适合使用它呢?

    有8位网友表示赞同!

颓废人士

Dijkstra算法简单易懂,我记得学过一次貌似。但其他几种算法听起来比较复杂。

    有9位网友表示赞同!

蔚蓝的天空〃没有我的翅膀

SPFA感觉在实际应用中会用到的机会多点吧。

    有7位网友表示赞同!

瑾澜

Bellman-Ford算法处理负权边是不是挺厉害的?

    有7位网友表示赞同!

红玫瑰。

我之前遇到过类似的最短路问题,不知道该用哪种算法比较好解决的问题。

    有11位网友表示赞同!

太难

这几个算法之间在时间复杂度和空间复杂度上有哪些区别呢?

    有9位网友表示赞同!

你很爱吃凉皮

图论真是个很强大的工具,可以用来解决很多现实问题啊。

    有9位网友表示赞同!

枫无痕

想了解一下这些算法具体的实现方法。

    有8位网友表示赞同!

减肥伤身#

有没有什么开源的代码例子可以参考一下?

    有15位网友表示赞同!

▼遗忘那段似水年华

我看了一下Floyd算法的时间复杂度是方程式的,怎么解释这个时间复杂度的吗?

    有20位网友表示赞同!

哥帅但不是蟋蟀

其实在实际生活中,最短路问题会经常遇到吧。比如导航软件就用到了最短路径算法吧。

    有15位网友表示赞同!

我的黑色迷你裙

请问学习这几个算法有什么难点?

    有16位网友表示赞同!

追忆思域。

哪个算法的理解难度相对最低呢?

    有7位网友表示赞同!

铁树不曾开花

可以来个简要对比一下吗,哪种算法更适合解决哪些类型的最短路问题?

    有19位网友表示赞同!

全网暗恋者

Floyd算法能处理多重边吗?

    有13位网友表示赞同!

一点一点把你清空

Dijkstra算法的数据结构是什么样的?

    有18位网友表示赞同!

此刻不是了i

SPFA算法的稳定性如何呢?

    有5位网友表示赞同!

素婉纤尘

Bellman-Ford算法对负权重的图处理是怎么实现的呢?

    有16位网友表示赞同!

烟雨离殇

什么时候需要用到这几个最短路算法呢?有什么实际的应用场景吗?

    有9位网友表示赞同!

【图论中最短路算法性能对比:Floyd、Dijkstra、SPFA、Bellman算法详解】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活