SPFA算法

Posted dwvictor

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SPFA算法相关的知识,希望对你有一定的参考价值。

适用于:
稀疏图(侧重于对边的处理)。

时间复杂度:
O(KE),K是常数,平均值为二,E是边数。(因为和边有关,所以不适于稠密图)

来源:
SPFA是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。 
这个算法简单地说就是队列优化的Bellman-Ford,利用了每个点不会更新次数太多的特点发明的此算法。 
SPFA在形式上和广度优先搜索非常类似,不同的是广度优先搜索中的一个点出了队列就不可能重新进入队列,但是SPFA中的一个点可能在出队列之后再次被放入队列,也就是说一个点修改过其他的点之后,过了一段时间可能会获得更短的路径,于是再次用来修改其他的点,这样反复进行下去。

优化方法:
1.循环队列(可以降低队列大小) 
2.SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j) < dist(i),则将j插入队首,否则插入队尾。

if(!vis[temp])
{
    if(dis[q[head + 1]] < dis[temp])  //注意小于号不要写反,否则时间会爆
      {
        tail = (++tail - 1) % qxun + 1;
        q[tail] = temp;
      }
    else
      {
        q[head] = temp;
        if(--head == 0) head = qxun;
      }
    vis[temp] = 1;
}

3.LLL:Large Label Last 策略,设队首元素为i,每次弹出时进行判断,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出对进行松弛操作。

实现:
(伪代码)

dis[i]记录从起点s到i的最短路径,w[i][j]记录连接i,j的边的长度,pre[v]记录前趋。
team[1..n]为队列,头指针head,尾指针tail。
布尔数组exist[1..n]记录一个点是否现在存在队列中。
初始化:dis[s] = 0, dis[v] = oo(v != s), memset(exist, false, sizeof(exist));
起点入队 team[1] = s; head = 0; tail = 1; exist[s] = true;
do
  {
    1.头指针向下移一位,取出指向的点u。
    2.exist[u] = false; 已经被取出了队列。
    3.for与u相连的所有点v  //注意不要去枚举所有点,用链式前向星存储
      if(dis[v] > dis[u] + w[u][v])
        {
          dis[v] = dis[u] + w[u][v];
          pre[v] = u;
          if(!exist[v]) //队列中不存在v点,v入队
            {
              尾指针下移一位,v入队;
              exist[v] = true;
            }
          }
  }while(head < tail);


--------------------- 

以上是关于SPFA算法的主要内容,如果未能解决你的问题,请参考以下文章

SPFA算法以及负环判断模板

SPFA最短路算法

算法描述》关于SPFA和Dijkstra算法的两三事

题目1008:最短路径问题(SPFA算法)

最短路径——SPFA算法(C++)

hdoj2544 最短路(Dijkstra || Floyd || SPFA)