博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅谈SPFA(队列优化的Bellman-Ford算法)
阅读量:4362 次
发布时间:2019-06-07

本文共 2104 字,大约阅读时间需要 7 分钟。

浅谈SPFA及其优化

  1. SPFA的前提-----Bellman-Ford算法
  2. SPFA的核心思想
  3. SPFA详解
  4. SPFA的优化
  5. 图中负环的判断

一、Bellman-Ford算法

Bellman-Ford 算法是一种用来求单源最短路径的一种算法,可以用于负边权上,但是如果有负环的话就没办法了(有负环可能算的出来吗?)
优点:便于理解,代码简短;
缺点:时间复杂度较高(保证为VE(边数*点数)),容易TLE,要仔细注意题的数据范围

Bellman-Ford算法的核心操作就是松弛,如果dis [ i ] + length [ i ] [ j ] < dis [ j ] ,就用dis [ i ] + length [ i ] [ j ] 更新dis [ j ] ;

算法步骤,枚举每个点,用每条边对其进行松弛操作,使答案不断逼近最优解,运行V-1次结束

算法正确性证明请参考
核心代码

for(int i=1;i<=n-1;i++){	for(int j=1;j<=m;j++)	{		if(dis[u[j]]+val[j]

当然我们可以考虑一个小优化,当循环到某个点i时已经无法松弛的时候,直退出循环

优化后的代码

for(int i=1;i<=n-1;i++){	int flag=0;	for(int j=1;j<=m;j++)	{		if(dis[u[j]]+val[j] < dis[v[j]])		{			dis[v[j]]=dis[u[j]]+val[j];			flag=1;//判断是否已经松弛完了 		} 	}	if(flag==0) break;//松弛完了就直接退出,小优化; }

SPFA的思想

其实在国外并不承认SPFA,只是将这个算法称作队列优化的Bellman-Ford算法,其实也是显而易见的,但是因为在中国,SPFA算法的发明者段凡丁是独自发现的,其实在国外早就有了类似的优化
SPFA的思想其实很简单,从起点开始向其他点进行松弛,并把松弛后的点加入队列中,这样对所有点进行松弛,SPFA复杂度在随机数据下的复杂度约为O(kE)(k是一个很小的常数)
但在最坏情况下,SPFA的复杂度任仍下降到O(VE),所以在正权图中更推荐效率更高的dijkstra算法(当然如果dijkstra算法超时的话也只能用SPFA了)但SPFA和Bellman-Ford算法一样可以在负边上运行。

SPFA详解+代码(丑的话不要介意)

SPFA大概的算法步骤已经有所介绍了,就是从起点开始按照深度对每个点进行松弛操作,将松弛后的点逐个加入队列里,当队列里所有点都已经操作后便结束算法;

核心代码如下

inline void spfa(int s){    fill(dis+1,dis+n+1,2147483647);	memset(vis,false,sizeof(vis));    dis[s]=0,que[1]=s,vis[s]=true;    int head=0,tail=1,u;    while(head

SPFA的小优化

SPFA主要有两种优化策略,SLF和LLL,介绍引自百度百科

SPFA算法有两个优化策略SLF和LLL——SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)< dist(i),则将j插入队首,否则插入队尾; LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出队进行松弛操作。SLF 和 LLF 在随机数据上表现优秀,但是在正权图上最坏情况为 O(VE),在负权图上最坏情况为达到指数级复杂度。

按照个人经验,随机数据下两种优化的效率大概为25%~40%左右,

实现较为简单,就不想贴代码了;

图中负环的判断

Bellman-Ford的判别方法很简单,对所有点松弛完后,再枚举所有边,如果有一条边仍能被松弛,那么说明图中有负环

代码

inline void bell(){	for(int i=1;i<=n-1;i++)//松弛操作    {    	int zgs=0;    	for(int j=1;j<=m;j++)	    {	    	if(dis[u[j]]+val[j]
dis[u[i]]+val[i]) { falg=0; break; } }}

而对于SPFA来说,一般有两种判断负环的方法

1、记录每个点入队的次数,如果某个点超过了N次,则存在负环

2、记录每个路径经过点的数量,如果存在某条路径经过的点的数量超过了N次,那么也说明存在负环

相比较而言,个人更偏向与第二种方法,实际数据对比的话第二种方法要比第一种方法要快那么一些,所以更偏向第二种方法

转载于:https://www.cnblogs.com/stargazer-cyk/p/10366534.html

你可能感兴趣的文章
char * 与char []探究理解
查看>>
QT窗体显示在屏幕中间位置
查看>>
emmet使用技巧
查看>>
RPC-Thrift(二)
查看>>
MSSQL for Linux 安装指南
查看>>
【Golang 接口自动化08】使用标准库httptest完成HTTP请求的Mock测试
查看>>
前端必读:浏览器内部工作原理
查看>>
Uri、URL和URN三者的区别
查看>>
数据字典的转换
查看>>
关于动态添加iview admin路由以及刷新侧边栏
查看>>
ApplicationInsights的探测器尝鲜
查看>>
java 解析Json格式数据
查看>>
unix中的线程池技术详解
查看>>
CSS简介
查看>>
常用三大软件评价1
查看>>
MVC各层介绍使用---初步理解
查看>>
单例对象的创建与销毁
查看>>
知识点关键词(记录一下)
查看>>
国际结算业务
查看>>
嵌套循环概念
查看>>