当前位置:首页 » 操作系统 » SPFA算法

SPFA算法

发布时间: 2022-02-10 05:59:41

① SPFA算法的pascal代码

SPFA——Shortest Path Faster Algorithm,它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。SPFA的实现甚至比Dijkstra或者Bellman_Ford还要简单:
设Dist[I]代表S到I点的当前最短距离,Fa[I]代表S到I的当前最短路径中I点之前的一个点的编号。开始时Dist全部为+∞,只有Dist[S]=0,Fa全部为0。

维护一个队列,里面存放所有需要进行迭代的点。初始时队列中只有一个点S。用一个布尔数组记录每个点是否处在队列中。

每次迭代,取出队头的点v,依次枚举从v出发的边v->u,设边的长度为len,判断Dist[v]+len是否小于Dist[u],若小于则改进Dist[u],将Fa[u]记为v,并且由于S到u的最短距离变小了,有可能u可以改进其它的点,所以若u不在队列中,就将它放入队尾。这样一直迭代下去直到队列变空,也就是S到所有的最短距离都确定下来,结束算法。

SPFA在形式上和宽度优先搜索非常类似,不同的是宽度优先搜索中一个点出了队列就不可能重新进入队列,但是SPFA中一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本身被改进,于是再次用来改进其它的点,这样反复迭代下去。设一个点用来作为迭代点对其它点进行改进的平均次数为k,有办法证明对于通常的情况,k在2左右(怎么证明的作者也不知道)。
const maxl=maxlongint;
var
g:array[1..100,1..100]of shortint;
b:array[1..1000]of boolean;
h,dis:array[1..1000]of longint;
x,s,e,i,j:longint;

procere inp;
begin
assign(input,'spfa.txt');reset(input);
readln(x,s,e);
for i:=1 to x do begin
for j:=1 to x do read(g[i,j]);
readln;
end;

for i:=1 to 1000 do dis[i]:=maxl;
fillchar(b,sizeof(b),false);
end;

procere spfa;
var
head,tail:longint;
procere relax(i:longint);
begin
if dis[i]>dis[h[head]]+g[h[head],i] then begin
if i=s then begin writeln('NO WAY');halt;end;{judge <0}
dis[i]:=dis[h[head]]+g[h[head],i];
if not b[i] then begin
inc(tail);
h[tail]:=i;
b[i]:=true;
end;
end;
end;
begin
head:=1;tail:=1;
h[1]:=s;
dis[s]:=0;
b[s]:=true;
repeat
for i:=1 to x do
if g[h[head],i]<>100 then relax(i);
b[h[head]]:=false;
inc(head);
until head>tail;
end;

begin
inp;
spfa;
writeln(dis[e]);
end.

② 用spfa算法求最短路,如果图中有负权环应如何处理

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;

⑤ 在使用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

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

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

⑧ 关于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算法的原理及证明

求单源最短路的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算法哪里有错误

欧拉回路
const maxn=100;
var
g:array[1..maxn,1..maxn] of longint;
:array[1..maxn] of longint;
circuit:array[1..maxn] of longint;
n,circuitpos,i,j,start,oddnumber:longint;

procere setIO;
begin
assign(input,'one.in');
reset(input);
assign(output,'one.out');
rewrite(output);
end;

procere find_circuit(i:longint);
var j:longint;
begin
for j:=1 to n do
if g[i,j]=1 then
begin
g[i,j]:=0;
g[j,i]:=0;
find_circuit(j);
end;
circuitpos:=circuitpos+1;
circuit[circuitpos]:=i;
end;

begin
// setIO;
read(n);
for i:=1 to n do
begin
[i]:=0;
for j:=1 to n do
begin
read(g[i,j]);
[i]:=[i]+g[i,j];
end;
end;

start:=1; oddnumber:=0;
for i:=1 to n do
if [i] mod 2 =1 then
begin
start:=i;
oddnumber:=oddnumber+1;
end;

if (oddnumber>2)or(oddnumber=1)
then writeln('No Solution!')
else begin
circuitpos:=0;
find_circuit(start);
for i:=1 to circuitpos-1 do write(circuit[i],'--->');
writeln(circuit[circuitpos]);
end;
close(input);close(output);
end.
SPFA
实现方法:建立一个队列,初始时队列里只有起始点,在建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空
判断有无负环:如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)
program spfaprg;
const
maxp=10000;
var
p,c,s,t:longint;
a,b:array[1..maxp,0..maxp] of longint;
d:array[1..maxp] of integer;
v:array[1..maxp] of boolean;
dist:array[1..maxp] of longint;
head,tail:longint;

procere init;
var
i,x,y,z:longint;
begin
read(p,c);
for i := 1 to c do
begin
readln(x,y,z);
inc(b[x,0]); b[x,b[x,0]] := y; a[x,y] := z;
inc(b[y,0]); b[y,b[y,0]] := x; a[y,x] := z;
end;
readln(s,t);
end;

procere spfa(s:longint); {SPFA}
var i,,j,now,sum:longint;
begin
fillchar(d,sizeof(d),0);
fillchar(v,sizeof(v),false);
for j := 1 to p do
dist[ j ]:=maxlongint;
dist[s] := 0; v[s] := true; d[1] := s;
head := 1; tail := 1;
while head<=tail do
begin
now := d[head];
for i := 1 to b[now,0] do
if dist[b[now,i]]>dist[now]+a[now,b[now,i]] then
begin
dist[b[now,i]]:= dist[now]+a[now,b[now,i]];
if not v[b[now,i]] then
begin
inc(tail);
d[tail] := b[now,i];
v[b[now,i]] := true;
end;
end;
v[now] := false;
inc(head);
end;
end;

procere print;
begin
writeln(dist[t]);
end;

begin
init;
spfa(s);
print;
end.

热点内容
海康威视存储卡质量如何 发布:2024-09-19 08:55:35 浏览:939
python3默认安装路径 发布:2024-09-19 08:50:22 浏览:514
环卫视频拍摄脚本 发布:2024-09-19 08:35:44 浏览:417
sqlserveronlinux 发布:2024-09-19 08:16:54 浏览:256
编程常数 发布:2024-09-19 08:06:36 浏览:952
甘肃高性能边缘计算服务器云空间 发布:2024-09-19 08:06:26 浏览:162
win7家庭版ftp 发布:2024-09-19 07:59:06 浏览:717
数据库的优化都有哪些方法 发布:2024-09-19 07:44:43 浏览:269
知乎华为编译器有用吗 发布:2024-09-19 07:32:20 浏览:618
访问虚拟机磁盘 发布:2024-09-19 07:28:13 浏览:670