导航:首页 > 源码编译 > SPFA算法

SPFA算法

发布时间:2022-02-04 02:25:26

① SPFA算法的原理及证明

求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm,是西南交通大学段凡丁于1994年发表的。从名字我们就可以看出,这种算法在效率上一定有过人之处。很多时候,给定的图存在负权边,这时类似Dijkstra算法等便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。简洁起见,我们约定加权有向图G不存在负权回路,即最短路径一定存在。如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。我们用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
定理:只要最短路径存在,上述SPFA算法必定能求出最小值。证明:每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。
期望时间复杂度:O(me), 其中m为所有顶点进队的平均次数,可以证明m一般小于等于2:“算法编程后实际运算情况表明m一般没有超过2n.事实上顶点入队次数m是一个不容易事先分析出来的数,但它确是一个随图的不同而略有不同的常数.所谓常数,就是与e无关,与n也无关,仅与边的权值分布有关.一旦图确定,权值确定,原点确定,m就是一个确定的常数.所以SPFA算法复杂度为O(e).证毕.(SPFA的论文)不过,这个证明是非常不严谨甚至错误的,事实上在bellman算法的论文中已有这方面的内容,所以国际上一般不承认SPFA算法。
对SPFA的一个很直观的理解就是由无权图的BFS转化而来。在无权图中,BFS首先到达的顶点所经历的路径一定是最短路(也就是经过的最少顶点数),所以此时利用数组记录节点访问可以使每个顶点只进队一次,但在带权图中,最先到达的顶点所计算出来的路径不一定是最短路。一个解决方法是放弃数组,此时所需时间自然就是指数级的,所以我们不能放弃数组,而是在处理一个已经在队列中且当前所得的路径比原来更好的顶点时,直接更新最优解。
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 可使速度提高 15 ~ 20%;SLF + LLL 可提高约 50%。 在实际的应用中SPFA的算法时间效率不是很稳定,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法。

② 在使用spfa算法一定可以找出最短路径吗

最开始队列里只有一个起始点,在你处理你选的“第一个点”之前,必须要先处理完起始点,这时队列里会有所有跟起始点相连的节点。然后按照你说的处理,第一点出列,然后不会有新的点加入,但是队列里还有其他跟起始点相连的,所以一般不会为空,算法继续执行。如果队列为空,则说明没有点跟起始点相连了,那么算法也就可以终止了

③ dijkstra和spfa相比,那个更好一些

SPFA的时间复杂度其实是在胡扯。在Bellman-Ford的论文里提及了队列优化,也就是现在的SPFA。k八成以上是瞎编的。原论文如下:

算法编程后实际运算情况表明m一般没有超过2n.事实上顶点入队次数m是一个不容易事先分析出来的数,但它确是一个随图的不同而略有不同的常数.所谓常数,就是与e无关,与n也无关,仅与边的权值分布有关.一旦图确定,权值确定,原点确定,m就是一个确定的常数.所以SPFA算法复杂度为O(e)

如果真的如这个所说,单源最短路径的时间复杂度是O(1)。

其实spfa真正复杂度是O(n^2)

带堆优化的dijkstra时间复杂度很低,为O(n logn),比较一下就可以得出dijkstra更好,不会被卡。

然后呢……在NOI2018 DAY1T1的归程中,spfa被万恶(良心)的出题人卡了,这也意味着以后的比赛将会hack SPFA,所以退spfa入dijkstra+堆优化保平安

我是菜鸡OIer chen_zhe

④ spfa算法 无向图中,一定要把检查过的边删除,不然会重新检查,形成死循环!

如果不做标记的话,访问过的点虽然不能再更新其它值了,但是还会不停访问下一个节点

⑤ Floyed算法,spfa算法,dij算法各自的优势都在哪里哪个适用于无向图哪个适用于负权边急!

直觉感觉是迪杰斯特拉的比较好。。。留个名。

⑥ 来解释下spfa和Dijkstra的优缺点

SPFA并不比Dijkstra慢多少
而且SPFA好写 还能做负权、判负环
比赛的时候掌握Floyd和SPFA就足够了

⑦ SPFA算法可否取代Dijkstra算法成为计算单源最短路径的最优解

SPFA在稀疏图上快,因为是通过边来增广的。dijkstra在稠密图上快。因为是通过点来增广的。某些情况下dijkstra 加上堆优化,在处理大数据的时候会比SPFA快很多;但是SPFA在随机数据的综合表现中相比dijkstra优势还是比较大的。总而言之,各有所长。

⑧ SPFA算法的伪代码

SPFA实际上是Bellman-Ford基础上的队列优化
一种伪代码 ProcereSPFA;Begininitialize-single-source(G,s);initialize-queue(Q);enqueue(Q,s);whilenotempty(Q)dobeginu:=dequeue(Q);foreachv∈adj[u]dobegintmp:=d[v];relax(u,v);if(tmp<>d[v])and(notvinQ)thenenqueue(Q,v);end;end;End;一种更容易读懂的伪代码: ProcereSPFA;Begininitialize-single-source(G,s);initialize-queue(Q);enqueue(Q,s);whilenotempty(Q)dobeginu:=dequeue(Q);foreachv∈adj[u]dobegintmp:=d[v];relax(u,v);if(tmp<>d[v])and(notvinQ)thenenqueue(Q,v);end;end;End;

⑨ 关于C++SPFA算法求最短路径的问题

用vs2017调试了一下,主要的问题就是初始化不完全,尤其是没有对vis数组初始化。在调试的过程中,把数据的输入改成文件形式了,不想弄成鼠标手^_^

#include"pch.h"
#define_CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
#include<iomanip>
#include<fstream>
usingnamespacestd;

constintSIZE=500;
intdis[SIZE]; //出发点到各点的最短估计距离
intpath[SIZE]; //路径上到达该点的上一顶点
inta[SIZE][SIZE]; //a[i][j]表示i到j路径的权值

//n为顶点数,s为出发点
//各顶点编号从1始起
voidspfa(intn,ints)
{
constintINF=999999; //初始最短路径估计值
intvis[SIZE]; //该点是否被访问过
intq[SIZE]; //用于实现spfa的队列

for(inti=1;i<=n;i++)
{
path[i]=-1;
dis[i]=INF;
vis[i]=0;
}
dis[s]=0;vis[s]=1;q[1]=s;

intv,head=0,tail=1;
while(head<tail)
{
head++;
v=q[head];
vis[v]=0; //出队

for(inti=1;i<=n;i++)
{
if(a[v][i]>0&&dis[i]>dis[v]+a[v][i])
{
dis[i]=dis[v]+a[v][i];
path[i]=v;
if(vis[i]==0)
{
tail++;
q[tail]=i;
vis[i]=1; //入队
}
}
}
}
}

//k为终点
voidprintpath(intk)
{
if(path[k]!=-1)
printpath(path[k]);
cout<<"->"<<k;
}

intmain()
{
#defineWsetw(4)

ifstreamin("in.txt");
if(!in.is_open())
{
cout<<"未打开in.txt,请检查文件是否在当前目录下";
return0;
}

intn,s; //顶点数,出发点
in>>n>>s;
cout<<"输入顶点数、出发点:"<<n<<""<<s<<endl;

cout<<"输入各条路径的起点、终点、权值:"<<endl;
inti=1;
while(in.peek()!=EOF) //in.eof()会多读一行
{
intx,y,w;
in>>x>>y>>w;
a[x][y]=w;
//a[y][x]=w;

cout<<"第("<<W<<i++<<")条边:";
cout<<W<<x<<W<<y<<W<<w<<endl;
}

cout<<endl;

spfa(n,s);
cout<<"从"<<s<<"到"<<n<<"的最短路径:"<<dis[n]<<endl;
printpath(n);

return0;
}

in文件内容:

111
125
133
241
253
266
358
367
376
486
498
583
595
693
6103
798
7104
8113
9112
10112

网络里面有个经典的c++ spfa程序,那个用了一点stl的知识,数组也从0始计数,你可以参考一下:

阅读全文

与SPFA算法相关的资料

热点内容
c编程用英文还是中文 浏览:723
一点都不解压的游戏 浏览:203
解压为什么不能用中文文件夹 浏览:615
服务器如何解除备份 浏览:144
安卓手机为什么用一年就变卡 浏览:11
如何用风变编程自动回复 浏览:512
安卓阅读币怎么样 浏览:437
京东app怎么切号 浏览:583
进入传奇服务器后如何修改 浏览:42
m0单片机的cycle怎么知道 浏览:806
linux命令太长 浏览:782
压缩机nb1111y是多少w 浏览:45
打赏视频用什么服务器好 浏览:154
方舟好友服务器怎么加mod 浏览:982
javaresponse设置编码 浏览:842
opc数据采集源码 浏览:563
命令女孩子 浏览:691
rtsp录像源码 浏览:388
加密狗复制啥意思 浏览:545
键盘文件夹重命名输入不了 浏览:413