【最短路算法】Dijkstra+heap和SPFA的区别

单源最短路问题(SSSP)常用的算法有Dijkstra,Bellman-Ford,这两个算法进行优化,就有了Dijkstra+heap、SPFA(Shortest Path Faster Algorithm)算法。这两个算法写起来非常相似。下面就从他们的算法思路、写法和适用场景上进行对比分析。如果对最短路算法不太了解,可先看一下相关ppt:最短路

为了解释得简单点,以及让对比更加明显,我就省略了部分细节。

我们先看优化前的:

\(O(V^2 + E)\)的Dijkstra

n-1次循环

-->找到未标记的d最小的点

-->标记,松弛它的边

\(O(VE)\)的Bellman-Ford

n-1次循环

-->对所有边松弛

还能再松弛则有负环

  • Dijkstra是每次确定了到一个点的最短距离,再用该点更新到其它点的距离。不能处理有负边的图。
  • Bellman-Ford是每次对所有边松弛。可以计算出有负边无负环的最短路,可以判断是否存在负环。

接下来再看优化后的:

\(O((V + E)lgV)\)的Dijkstra+heap优化

用STL中的优先队列实现堆:
while(优先队列非空)

-->队头出队,松弛它的边

-->松弛了的<新距离,点>入队

删了部分定义和初始化的代码:

typedef pair<int,int> PII;
priority_queue<PII,vector<PII>,greater<PII> > q;
...
while(!q.empty()){  // O(V) 加上count<n可以优化一点点 
    int w=q.top().first, u=q.top().second;
    q.pop();   // O(lgV)
    if(b[u])continue; b[u]=true;
    //++count;
    for(int i=head[u];i;i=e[i].next){ // Sum -> O(E)
        int v=e[i].to;
        if(d[u]+e[i].w<d[v]){
            d[v]=d[u]+e[i].w;
            q.push(PII(d[v],v));  // O(lgV)
        }
    }
}

\(O(kE)\)\(O(VE)\)的SPFA

while(队非空)

-->队头出队,松弛它的边

-->松弛了且不在队内的点入队

while(!q.empty()){
    int u=q.front(); q.pop();
    b[u]=false;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(d[u]+e[i].w<d[v]){
            d[v]=d[u]+e[i].w;
            if(!b[v])b[v]=true,q.push(v);
        }
    }
}

算法思路对比

  • Dijkstra+heap是用小根堆,每次取出d最小的点,来更新距离,那么这个点来说,最小距离就是当前的d。
  • SPFA是用双端队列,每次取出队头,来更新距离,它之后可能还会入队。它是一种动态逼近法,因为每次松弛距离都会减小,所以松弛一定会有结束的。如果一个点入队超过n次就是存在负环。

复杂度分析对比

Dijkstra+heap

  • 因为是堆,取队头需要O(lgV)。
  • 松弛边时,因为点的d改变了,所以点v需要以新距离重新入堆,O(lgV),总共O(ElgV)。
  • 因此总的是\(O((V + E)lgV)\)

SPFA

  • 论文证明也不严格。复杂度不太好分析。
  • 总的是O(kE)。k大概为2
  • 复杂度应该是 \(O(VE)\)

适用场景

如果是稠密图,Dijkstra+heap比SPFA快。稀疏图则SPFA更快。SPFA可以有SLF和LLL两种优化,SLF就是d比队头小就插入队头,否则插入队尾。

另外,Dijkstra和Prim也很相似,它们的区别主要是d的含义,前者是到s的临时最短距离,后者是到树的临时最短距离,相同点是,每次找d最小的更新其它点的距离。

posted @ 2017-05-10 01:50  水郁  阅读(12081)  评论(4编辑  收藏  举报
……