zoukankan      html  css  js  c++  java
  • 最短路径算法之四——SPFA算法

    SPAF算法

    求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm,该算法是西南交通大学段凡丁于1994年发表的。

    它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径。

    其中k为所有顶点进队的平均次数,可以证明k一般小于等于2,可以处理负边,但无法处理带负环的图(负环和负边不是一个概念)。

    SPFA的实现甚至比Dijkstra或者Bellman_Ford还要简单。


    SPFA算法过程
      我们记源点为S,由源点到达点i的“当前最短路径”为D[i],开始时将所有D[i]初始化为无穷大,D[S]则初始化为0。算法所要做的,就是在运行过程中,不断尝试减小D[]数组的元素,最终将其中  每一个元素减小到实际的最短路径。
      过程中,我们要维护一个队列,开始时将源点置于队首,然后反复进行这样的操作,直到队列为空:
      (1)从队首取出一个结点u,扫描所有由u结点可以一步到达的结点,具体的扫描过程,随存储方式的不同而不同;
      (2)一旦发现有这样一个结点,记为v,满足D[v] > D[u] + w(u, v),则将D[v]的值减小,减小到和D[u] + w(u, v)相等。其中,w(u, v)为图中的边u-v的长度,由于u-v必相邻,所以这个长度一定已知(不然我们得到的也不叫一个完整的图);这种操作叫做松弛。
      (3)上一步中,我们认为我们“改进了”结点v的最短路径,结点v的当前路径长度D[v]相比于以前减小了一些,于是,与v相连的一些结点的路径长度可能会相应地减小。注意,是可能,而不是一定。但即使如此,我们仍然要将v加入到队列中等待处理,以保证这些结点的路径值在算法结束时被降至最优。

    判断有无负环
      如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)
      对于不存在负权回路的图来说,上述算法是一定会结束的。因为算法在反复优化各个最短路径长度,总有一个时刻会进入“无法再优化”的局面,此时一旦队列读空,算法就结束了。然而,如果图中存  在一条权值为负的回路,就糟糕了,算法会在其上反复运行(因为d[]加上一个负数肯定变下了,所以在有负环的情况下,会不断有数进入队列),通过“绕圈”来无休止地试图减小某些相关点的最短路  径值。假如我们不能保证图中没有负权回路,一种“结束条件”是必要的。这种结束条件是什么呢?
      思考Bellman-Ford算法,它是如何结束的?显然,最朴素的Bellman-Ford算法不管循环过程中发生了什么,一概要循环|V|-1遍才肯结束。凭直觉我们可以感到,SPFA算法“更聪明一些”,就是说我  们可以猜测,假如在SPFA中,一个点进入队列——或者说一个点被处理——超过了|V|次,那么就可以断定图中存在负权回路了。

    SPFA代码实现(HDU1535为例)

     

     1 #include<stdio.h>
     2 #include<limits.h>
     3 #include<iostream>
     4 #include<string>
     5 #include<queue>
     6 #define MAXN 1000000
     7 using namespace std;
     8 struct e
     9 {
    10     int begin;
    11     int end;
    12     int dis;
    13 } edge1[MAXN+10],edge2[MAXN+10];
    14 int dis[MAXN+10],first[MAXN+10];
    15 bool vis[MAXN+10];
    16 int T,S,D,N,k,M;
    17 void SPFA(int begin,struct e edge[])
    18 {
    19     for (int i=1; i<=N; i++)
    20     {
    21         dis[i]=INT_MAX;
    22         vis[i]=0;
    23     }
    24     queue <int> Q;
    25     Q.push(begin);
    26     dis[begin]=0;
    27     while (!Q.empty())
    28     {
    29         begin=Q.front();
    30         Q.pop();
    31         vis[begin]=0;
    32         for (int i=first[begin]; edge[i].begin==begin; i++)
    33             if (dis[edge[i].end]>dis[begin]+edge[i].dis)
    34             {
    35                 dis[edge[i].end]=dis[begin]+edge[i].dis;
    36                 if (!vis[edge[i].end])
    37                 {
    38                     Q.push(edge[i].end);
    39                     vis[edge[i].end]=1;
    40                 }
    41             }
    42     }
    43 }
    44 void init(struct e edge[])
    45 {
    46     memset(first,0,sizeof(first));
    47     first[edge[1].begin]=1;
    48     for (int i=2; i<=M; i++)
    49         if (edge[i-1].begin!=edge[i].begin) first[edge[i].begin]=i;
    50 }
    51 bool cmp(struct e a,struct e b)
    52 {
    53     return a.begin<b.begin;
    54 }
    55 int main()
    56 {
    57     int T;
    58     cin>>T;
    59     while (T--)
    60     {
    61         scanf("%d %d",&N,&M);
    62         int x1,x2,x3;
    63         for (int i=1; i<=M; i++)
    64         {
    65             scanf("%d %d %d",&x1,&x2,&x3);
    66             edge1[i].begin=x1,edge1[i].end=x2,edge1[i].dis=x3;
    67             edge2[i].begin=x2,edge2[i].end=x1,edge2[i].dis=x3;
    68         }
    69         sort(edge1+1,edge1+M+1,cmp);
    70         sort(edge2+1,edge2+M+1,cmp);
    71         init(edge1);
    72         SPFA(1,edge1);
    73         int cnt=0;
    74         for (int i=1; i<=N; i++)
    75             cnt+=dis[i];
    76         init(edge2);
    77         SPFA(1,edge2);
    78         for (int i=1; i<=N; i++)
    79             cnt+=dis[i];
    80         printf("%d
    ",cnt);
    81     }
    82     return 0;
    83 }

     

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

     

  • 相关阅读:
    java并发编程(五)lock
    java并发编程(一)线程状态 & 线程中断 & 线程间的协作
    java基础之 clone
    java基础 小知识点汇总篇
    java并发编程(四) 线程池 & 任务执行、终止源码分析
    GC(一)内存管理与垃圾回收
    java并发编程(三)cpu cache & 缓存一致性
    java并发编程(八) CAS & Unsafe & atomic
    @PathVariable注解
    redis分布式锁
  • 原文地址:https://www.cnblogs.com/Enumz/p/3862690.html
Copyright © 2011-2022 走看看