求单源最短路SPFA算法.docx

上传人:b****7 文档编号:10279194 上传时间:2023-02-09 格式:DOCX 页数:15 大小:18.87KB
下载 相关 举报
求单源最短路SPFA算法.docx_第1页
第1页 / 共15页
求单源最短路SPFA算法.docx_第2页
第2页 / 共15页
求单源最短路SPFA算法.docx_第3页
第3页 / 共15页
求单源最短路SPFA算法.docx_第4页
第4页 / 共15页
求单源最短路SPFA算法.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

求单源最短路SPFA算法.docx

《求单源最短路SPFA算法.docx》由会员分享,可在线阅读,更多相关《求单源最短路SPFA算法.docx(15页珍藏版)》请在冰豆网上搜索。

求单源最短路SPFA算法.docx

求单源最短路SPFA算法

求单源最短路-SPFA算法

求单源最短路的SPFA算法的全称是:

ShortestPathFasterAlgorithm。

  SPFA算法是西南交通大学段凡丁于1994年发表的.

  从名字我们就可以看出,这种算法在效率上一定有过人之处。

  很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。

  简洁起见,我们约定有向加权图G不存在负权回路,即最短路径一定存在。

当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。

  我们用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。

我们采取的方法是动态逼近法:

设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。

这样不断从队列中取出结点来进行松弛操作,直至队列空为止。

  定理:

只要最短路径存在,上述SPFA算法必定能求出最小值。

  证明:

每次将点放入队尾,都是经过松弛操作达到的。

换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。

所以算法的执行会使d越来越小。

由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。

因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。

(证毕)

  期望的时间复杂度O(ke),其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。

  实现方法:

建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。

然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。

重复执行直到队列为空

  判断有无负环:

如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

[编辑本段]

伪代码

  SPFA实际上是Bellman-Ford基础上的优化

  SPFA(G,w,s)

  1.INITIALIZE-SINGLE-SOURCE(G,s)

  2.INITIALIZE-QUEUE(Q)

  3.ENQUEUE(Q,s)

  4.WhileNotEMPTY(Q)

  5.Dou<-DLQUEUE(Q)

  6.For每条边(u,v)inE[G]

  7.Dotmp<-d[v]

  8.Relax(u,v,w)

  9.If(d[v]

  10.ENQUEUE(Q,v)

  

  一种更容易读懂的伪代码

  ProcedureSPFA;

  Begin

  initialize-single-source(G,s);

  initialize-queue(Q);

  enqueue(Q,s);

  whilenotempty(Q)dobegin

  u:

=dequeue(Q);

  foreachv∈adj[u]dobegin

  tmp:

=d[v];

  relax(u,v);

  if(tmp<>d[v])and(notvinQ)thenenqueue(Q,v);

  end;

  end;

  End;

  

  期望的时间复杂度O(2e)

  

  对spfa的一个很直观的理解就是由无权图的bfs转化而来.在无权图中,bfs首先到达的顶点所经历的路径一定是最短路(也就是经过的最少顶点数).所以此时利用visit[u],可以使每个顶点只进队一次.在带权图中,最先到达的顶点所计算出来的路径不一定是最短路.一个解决方法是放弃visit数组,此时所需时间自然就是指数级的.所以我们不能放弃visit数组,而是在处理一个已经在队列中且当前所得的路径比原来更好的顶点时,直接更新最优解.

[编辑本段]

标准SPFA过程

  (以求某个结点t到某个结点s的最短路为例,稍加修改即为单源最短路)programspfaprg;

  

Pascal代码

  const

  maxp=10000;{最大结点数}

  var{变量定义}

  p,c,s,t:

longint;{p,结点数;c,边数;s:

起点;t:

终点}

  a,b:

array[1..maxp,0..maxp]oflongint;{a[x,y]存x,y之间边的权;b[x,c]存与x相连的第c个边的另一个结点y}

  d:

array[1..maxp]ofinteger;{队列}

  v:

array[1..maxp]ofboolean;{是否入队的标记}

  dist:

array[1..maxp]oflongint;{到起点的最短路}

  head,tail:

longint;{队首/队尾指针}

  procedureinit;

  var

  i,x,y,z:

longint;

  begin

  read(p,c);

  fori:

=1tocdo

  begin

  readln(x,y,z);{x,y:

一条边的两个结点;z:

这条边的权值}

  inc(b[x,0]);b[x,b[x,0]]:

=y;a[x,y]:

=z;{b[x,0]:

以x为一个结点的边的条数}

  inc(b[y,0]);b[y,b[y,0]]:

=x;a[y,x]:

=z;

  end;

  readln(s,t);{读入起点与终点}

  end;

  procedurespfa(s:

longint);{SPFA}

  var

  i,,j,now,sum:

longint;

  begin

  fillchar(d,sizeof(d),0);

  fillchar(v,sizeof(v),false);

  forj:

=1topdodist[j]:

=maxlongint;

  dist[s]:

=0;v[s]:

=true;d[1]:

=s;{队列的初始状态,s为起点}

  head:

=1;tail:

=1;

  whilehead<=taildo{队列不空}

  begin

  now:

=d[head];{取队首元素}

  fori:

=1tob[now,0]do

  ifdist[b[now,i]]>dist[now]+a[now,b[now,i]]then

  begin

  dist[b[now,i]]:

=dist[now]+a[now,b[now,i]];{修改最短路}

  ifnotv[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;

  procedureprint;

  begin

  writeln(dist[t]);

  end;

  begin

  init;

  spfa(s);

  print;

  end.

  

C语言代码

  #include

  #definemaxint2139062143

  inta[101][101],dist[101],n;

  voidspfa(ints)

  {

  intq[101],v[101],h=0,t=1,x,i;//q为队列,v为Boolean数组,表示结点是否在队列中,h为头指针,t为尾指针

  memset(q,0,sizeof(q));

  memset(v,0,sizeof(v));

  memset(dist,127,sizeof(dist));//置dist数组为maxint(+∞),下同

  dist[s]=0;

  q[t]=s;v[s]=1;

  while(h

  {

  h=(h+1)%n;

  x=q[h];

  v[x]=0;

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

  if(dist[i]>dist[x]+a[x][i])

  {

  dist[i]=dist[x]+a[x][i];

  if(!

v[i])

  {

  t=(t+1)%n;q[t]=i;v[i]=1;

  }

  }

  }

  }

  intmain()

  {

  intm,s,t,i;

  scanf("%d%d",&n,&m);

  scanf("%d%d",&s,&t);

  memset(a,127,sizeof(a));

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

  {

  intx,y,z;

  scanf("%d%d%d",&x,&y,&z);

  a[x][y]=z;

  a[y][x]=z;

  }

  spfa(s);

  printf("%d",dist[t]);

  system("pause");

  return0;

  }

[编辑本段]

SPFA的各种实现

  

最基本的前项性优化的spfa(有向图)

  复杂度O(ke).

  var

  a,b,e:

array[1..1000]oflongint;

  vis:

array[1..2000]ofboolean;

  q,d,f:

array[1..2001]oflongint;

  n,m,i,s,t:

longint;

  procedureqsort(l,r:

longint);

  vari,j,x,y:

longint;

  begin

  i:

=l;

  j:

=r;

  x:

=a[(l+r)shr1];

  repeat

  whilea[i]

  whilea[j]>xdodec(j);

  ifnot(i>j)thenbegin

  y:

=a[i];a[i]:

=a[j];a[j]:

=y;

  y:

=b[i];b[i]:

=b[j];b[j]:

=y;

  y:

=e[i];e[i]:

=e[j];e[j]:

=y;

  inc(i);

  dec(j);

  end;

  untili>j;

  ifi

  ifl

  end;

  procedurespfa(s:

longint);

  vari,k,l,t:

longint;

  begin

  fillchar(vis,sizeof(vis),0);

  fori:

=1tondod[i]:

=maxlongint;

  d[s]:

=0;

  l:

=0;

  t:

=1;

  q[1]:

=s;

  vis[s]:

=true;

  repeat

  l:

=lmod10000+1;

  k:

=q[l];

  fori:

=f[k]tof[k+1]-1do

  ifd[k]+e[i]

  begin

  d[b[i]]:

=d[k]+e[i];

  ifnotvis[b[i]]thenbegin

  t:

=tmod10000+1;

  q[t]:

=b[i];

  vis[b[i]]:

=true;

  end;

  end;

  vis[k]:

=false;

  untill=t;

  end;

  Begin

  readln(n,m);

  fori:

=1tomdo

  readln(a[i],b[i],e[i]);

  qsort(1,m);

  fori:

=1tomdo

  iff[a[i]]=0thenf[a[i]]:

=i;

  f[n+1]:

=m+1;

  fori:

=ndownto1do

  iff[i]=0thenf[i]:

=f[i+1];

  readln(s,t);

  spfa(s);

  writeln(d[t]);

  end.

  

堆排实现(无向图)

  

  constMM=1000;

  var

  a,b,e:

array[1..MM]oflongint;

  q,d,f:

array[1..2*MM+1]oflongint;

  vis:

array[1..MM]ofboolean;

  n,m,i,s,t:

longint;

  mh:

longint;

  procedureheapify(i:

longint);

  varl,r,max,temp:

longint;

  begin

  l:

=2*i;

  r:

=l+1;

  max:

=i;

  if(l<=mh)and(a[l]>a[max])thenmax:

=l;

  if(r<=mh)and(a[r]>a[max])thenmax:

=r;

  ifmax<>ithenbegin

  temp:

=a[i];a[i]:

=a[max];a[max]:

=temp;

  temp:

=b[i];b[i]:

=b[max];b[max]:

=temp;

  temp:

=e[i];e[i]:

=e[max];e[max]:

=temp;

  heapify(max);

  end;

  end;

  procedureheap;

  vari,temp:

longint;

  begin

  mh:

=m;

  fori:

=(mdiv2)downto1doheapify(i);

  fori:

=mdownto2dobegin

  temp:

=a[i];a[i]:

=a[1];a[1]:

=temp;

  temp:

=b[i];b[i]:

=b[1];b[1]:

=temp;

  temp:

=e[i];e[i]:

=e[1];e[1]:

=temp;

  dec(mh);

  heapify

(1);

  end;

  end;

  procedurespfa(s:

longint);

  vari,k,l,t:

longint;

  begin

  fillchar(vis,sizeof(vis),false);

  fori:

=1tondod[i]:

=maxlongint;

  d[s]:

=0;

  l:

=0;

  t:

=1;

  vis[s]:

=true;

  q[1]:

=s;

  repeat

  l:

=(lmod(2*MM))+1;

  k:

=q[l];

  fori:

=f[k]tof[k+1]-1do

  ifd[k]+e[i]

  begin

  d[b[i]]:

=d[k]+e[i];

  ifnotvis[b[i]]thenbegin

  t:

=(tmod(2*MM))+1;

  q[t]:

=b[i];

  vis[b[i]]:

=true;

  end;

  end;

  vis[k]:

=false;

  untill=t;

  end;

  Begin

  readln(n,m);

  fori:

=1tomdobegin

  readln(a[i],b[i],e[i]);

  a[i+m]:

=b[i];

  b[i+m]:

=a[i];

  e[i+m]:

=e[i];

  end;

  m:

=m+m;

  heap;

  fori:

=1tomdoiff[a[i]]=0thenf[a[i]]:

=i;

  f[n+1]:

=m+1;

  fori:

=ndownto1doiff[i]=0thenf[i]:

=f[i+1];

  readln(s,t);

  spfa(s);

  writeln(d[t]);

  end.

  

邻接矩阵(无向图)

  以上两种是边集数组储存方式,以下是邻接矩阵(无向图):

  constMM=100*2;

  var

  n,m,i,j:

longint;

  g:

array[1..MM,1..MM]oflongint;

  q,d:

array[1..MM]oflongint;

  vis:

array[1..MM]ofboolean;

  a,b,e,s,t:

longint;

  procedurespfa(s:

longint);

  varl,t,i,k:

longint;

  begin

  fillchar(vis,sizeof(vis),false);

  fori:

=1tondod[i]:

=maxlongint;

  d[s]:

=0;

  l:

=0;

  t:

=1;

  q[1]:

=s;

  vis[s]:

=true;

  repeat

  l:

=lmodMM+1;

  k:

=q[l];

  fori:

=1tondo

  if(g[k,i]

  d[i]:

=d[k]+g[k,i];

  ifnotvis[i]thenbegin

  t:

=tmodMM+1;

  q[t]:

=i;

  vis[i]:

=true;

  end;

  end;

  vis[k]:

=false;

  untill=t;

  end;

  begin

  readln(n,m);

  fori:

=1tondo

  forj:

=1tondog[i,j]:

=maxlongint;

  fori:

=1tomdo

  begin

  readln(a,b,e);

  g[a,b]:

=e;

  g[b,a]:

=e;

  end;

  readln(s,t);

  spfa(s);

  writeln(d[t]);

  end.

扩展阅读:

1.

2.

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 外语学习 > 法语学习

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1