zoukankan      html  css  js  c++  java
  • 【最短路算法】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最小的更新其它点的距离。

  • 相关阅读:
    IDEA-各模块间引用出现问题的解决方法
    【MyBatis学习06】_parameter:解决There is no getter for property named in class java.lang.String
    《转载》JVM垃圾回收机制
    java面试复习题四
    Java中excel转换为jpg/png图片 采用aspose-cells-18.6.jar
    POI导出复杂的excel;excel公共样式类;excel拼接定制类;数据科学计数法转为普通值
    java发送邮件无法显示图片 图裂 的解决办法
    pom.xml文件最详细的讲解
    Tomcat启动报Error listenerStart错误 Context [] startup failed due to previous errors
    ora-01031:insufficient privileges解决方法
  • 原文地址:https://www.cnblogs.com/flipped/p/6830073.html
Copyright © 2011-2022 走看看