参考

有空再更新下用c++, 下面用的Java

Dijkstra:适用于权值为非负的图的单源最短路径,用斐波那契堆的复杂度O(E+VlgV)

BellmanFord:适用于权值有负值的图的单源最短路径,并且能够检测负圈,复杂度O(VE)
SPFA:适用于权值有负值,且没有负圈的图的单源最短路径,论文中的复杂度O(kE),k为每个节点进入Queue的次数,且k一般<=2,但此处的复杂度证明是有问题的,其实SPFA的最坏情况应该是O(VE).
Floyd:每对节点之间的最短路径。

先给出结论:
(1)当权值为非负时,用Dijkstra。
(2)当权值有负值,且没有负圈,则用SPFA,SPFA能检测负圈,但是不能输出负圈。
(3)当权值有负值,而且可能存在负圈,则用BellmanFord,能够检测并输出负圈。
(4)SPFA检测负环:当存在一个点入队大于等于V次,则有负环,后面有证明。

本文针对SPFA算法进行分析。


本文解决问题有:
(1)证明SPFA算法最坏复杂度。
(2)为什么存在一个点进入队列V次,就说图有负环。

SPFA是西安交通大学的段凡丁在1994年与《西安交通大学学报》中发表的“关于最短路径的SPFA快速算法”,他在里面说SPFA速度比Dijkstra快,且运行V次的SPFA速度比Floyd速度快,当时我就产生了疑惑:为什么他这么快,在一些经典的书籍中都没有出现过,也没被提及过。
事实证明SPFA算法是有局限的,他不适用于稠密图,对于特别情况的稠密图,SPFA复杂度和BellmanFord时间一样。

最优时间复杂度先不看。

下面来证明SPFA最坏时间复杂度:

思路:
(1)找出SPFA的最最坏到不可能的情况的复杂度为O(VE)。
(2)找出SPFA确实有图,使得跑SPFA的复杂度为O(VE)。


我原本想举一个例子来说明SPFA存在O(VE)的情况,但是确实,最坏情况复杂度是不能用举例说明的,谢谢TianMingBu老师的指出。


证明如果有负环当且仅当存在一个点入队列次数大于等于V次。

对于某个点v,我们已知s到v的松弛路径的边的数量最多为V-1。
我这里说的松弛路径指的是:比如s直接松弛v,这样就有一条松弛路径:s->v 。s松弛a,a松弛v,则s->a->v就是一条松弛路径。

对于所有s到v的松弛路径来说,当松弛路径边的数量相等时,v只入队一次。
比如有松弛路径:
s->a->x->v
s->b->x->v
s->c->z->v,可以看出v只入队一次。
因为s到v的松弛路径的长度最多可以有V-1种变化,所以v最多入队V-1次。

举个例子:

假设有一个图,点集为{s,a,b,c,v},则最多可能的松弛路径有:
s->v
s->a->v
s->b->v
s->c->v
s->a->b->v
s->a->c->v
s->b->c->v
s->a->b->c->v

则松弛路径的边数变化有1,2,3,4,所以v入队为4次,即V-1次。



所以我们可以说每个点最多入队V-1次,因此我们求最坏情况为每个点都入队V-1次,所以此时:


这里举个最坏情况的例子。



当然我们可能考虑,当给定一个V的值,E的值,比如E=2V,怎么给出一个图,使得对此图运行SPFA算法的复杂度为O(VE).
我们这里假定图是连通的,所以E>=V-1。

方法如下:
(1)我们首先将图组成一个链,即如下图所示:



这样就用去了V-1条边。
(2)分别添加v0连向v2,v3,....vk的边,我们要添加的这些边的权值要满足v0先更新vk,v0更新vk-1后vk-1还能更新vk,以此类推,如下图所示:

(3)以vk,vk-1,.....v1的顺序添加权值为正无穷的自环,且不断循环,这个步骤是为了保持v1到vk点的出度保持一致,所以这样做,如下图:


这样我们就构造了一个能够让SPFA跑出O(VE)的图了,原因如下:

因为我们E的值和V的值是不确定的,所以很有可能不能够完成上述的这些构造,我们会分析当没有剩余的边构造上面的步骤(2)时的复杂度(也就是说E<=2V-3,因为第一步连成一个链需要V-1条边,而第二步v0连出去的边需要V-2),和有足够的边能构造上面的图这两种情况。

(1)如果E<=2V-3

因为E<=2V-3,所以E=O(V),所以只要能够求出复杂度是O(V^2),即可说为O(VE).
我们要计算所有点的入队次数和访问的边数。

V0出度为 E-V+2,V0入队1次。
V1出度为1,V1入队为1次。
V2出度为1,V2入队为2次(分别为v0松弛v2,v1松弛v2)。
V3出度为1,V3入队为3次。
....
V(e-v+2)出度为1,入队次数为(E-V+2)次。
后面V(e-v+3),V(e-v+4),.....V(k-1)的出度为1,入队次数为E-V+2次。 这些点的个数为V-(E-V+3)-1 = 2V-E-4。
vk出度为0,vk入队为E-V+2次。

所以总共的访问的边数为:


(2)如果E>2V-3

此时构造图的第二步已经完毕,所以后面剩余的边只需要不断添加自环保持出度平衡即可。

V0出度为V-1,入队1次。
V1到Vk出度为(E-V+2)/(V-1) 或(E-V+2)/(V-1)+1。
v1到vk的入队次数分别为1,2,3,.....V-1。

所以总共访问边数为:




[java] view
plain
copy

  1. package C24;
  2. import java.util.Iterator;
  3. import java.util.LinkedList;
  4. import java.util.List;
  5. import C22.GraphFactory;
  6. import C22.Pair;
  7. import C22.Weighted_Adjacent_List;
  8. public class SPFA {
  9. public int[] spfa(Weighted_Adjacent_List G,String s){
  10. return spfa(G,G.getVertexIndex(s));
  11. }
  12. public int[] spfa(Weighted_Adjacent_List G,int s){
  13. //1.创建所要的数据结构
  14. int size = G.getSize();
  15. int d[] = new int[size];    //距离估计
  16. for(int i=0;i<d.length;i++){
  17. d[i] = Integer.MAX_VALUE;
  18. }
  19. List<Integer> Q = new LinkedList<Integer>();
  20. boolean is_in_queue[] = new boolean[size];  //是否在队列中
  21. for(int i=0;i<is_in_queue.length;i++){
  22. is_in_queue[i] = false;
  23. }
  24. //2.初始化
  25. d[s] = 0;
  26. Q.add(s);
  27. is_in_queue[s] = true;
  28. //3.核心
  29. while(!Q.isEmpty()){
  30. int u = Q.remove(0);
  31. is_in_queue[u] = false;
  32. List<Pair> list = G.getListByVertexIndex(u);
  33. Iterator<Pair> iter = list.iterator();
  34. while(iter.hasNext()){
  35. Pair vstr = iter.next();
  36. int v = G.getVertexIndex(vstr.end);
  37. if(d[v]>d[u]+vstr.weight){
  38. d[v] = d[u] + vstr.weight;
  39. if(!is_in_queue[v]){    //如果松弛的点不在队列中,则加入队列;如果在队列中,则不动
  40. Q.add(v);
  41. is_in_queue[v] = true;
  42. }
  43. }
  44. }
  45. }
  46. return d;
  47. }
  48. public static void main(String[] args) throws Exception {
  49. SPFA spfa_alg = new SPFA();
  50. Weighted_Adjacent_List g = GraphFactory.getWeightedAdjacentListInstance("input\\weighted_graph.txt");
  51. int[] d = spfa_alg.spfa(g,"s");
  52. for(int i=0;i<d.length;i++){
  53. System.out.println(g.getVertexValue(i)+":"+d[i]);
  54. }
  55. }
  56. }

版权声明:本文为博主原创文章,未经博主允许不得转载。

Dijkstra、Bellman_Ford、SPFA、Floyd算法复杂度比较的更多相关文章

  1. 【转载】Dijkstra算法和Floyd算法的正确性证明

      说明: 本文仅提供关于两个算法的正确性的证明,不涉及对算法的过程描述和实现细节 本人算法菜鸟一枚,提供的证明仅是自己的思路,不保证正确,仅供参考,若有错误,欢迎拍砖指正   ----------- ...

  2. Dijkstra算法和Floyd算法的正确性证明

    说明: 本文仅提供关于两个算法的正确性的证明,不涉及对算法的过程描述和实现细节 本人算法菜鸟一枚,提供的证明仅是自己的思路,不保证正确,仅供参考,若有错误,欢迎拍砖指正   ------------- ...

  3. 最短路径——Dijkstra算法和Floyd算法

    Dijkstra算法概述 Dijkstra算法是由荷兰计算机科学家狄克斯特拉(Dijkstra)于1959 年提出的,因此又叫狄克斯特拉算法.是从一个顶点到其余各顶点的最短路径算法,解决的是有向图(无 ...

  4. 最短路径Dijkstra算法和Floyd算法整理、

    转载自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html 最短路径—Dijkstra算法和Floyd算法 Dijks ...

  5. 【转】最短路径——Dijkstra算法和Floyd算法

    [转]最短路径--Dijkstra算法和Floyd算法 标签(空格分隔): 算法 本文是转载,原文在:最短路径-Dijkstra算法和Floyd算法 注意:以下代码 只是描述思路,没有测试过!! Di ...

  6. 四大算法解决最短路径问题(Dijkstra+Bellman-ford+SPFA+Floyd)

    什么是最短路径问题? 简单来讲,就是用于计算一个节点到其他所有节点的最短路径. 单源最短路算法:已知起点,求到达其他点的最短路径. 常用算法:Dijkstra算法.Bellman-ford算法.SPF ...

  7. 最短路径—Dijkstra算法和Floyd算法

    原文链接:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html 最后边附有我根据文中Dijkstra算法的描述使用jav ...

  8. 最短路径—大话Dijkstra算法和Floyd算法

    Dijkstra算法 算法描述 1)算法思想:设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , ...

  9. 最短路径—Dijkstra算法和Floyd算法【转】

    本文来自博客园的文章:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html Dijkstra算法 1.定义概览 Dijk ...

随机推荐

  1. 关于IScroll使用中的常见问题与解决方案

    1.在iscroll4的滚动容器范围内,点击input框.select等表单元素时没有响应这个问题原因在于iscroll需要一直监听用户的touch操作,以便灵敏的做出对应效果,所以它把其余的默认事件 ...

  2. Redis笔记 -- 链表和链表节点的API函数(三)

    链表和链表节点API 函数 作用 时间复杂度 listSetDupMethod 将给定的函数设置为链表的节点值复制函数 复制函数可以通过链表的dup属性直接获得,O(1) listGetDupMeth ...

  3. 大数据时代数据库-云HBase架构&生态&实践

    业务的挑战 存储量量/并发计算增大 现如今大量的中小型公司并没有大规模的数据,如果一家公司的数据量超过100T,且能通过数据产生新的价值,基本可以说是大数据公司了 .起初,一个创业公司的基本思路就是首 ...

  4. 嵌入式C语言自我修养 08:变参函数的格式检查

    8.1 属性声明:format GNU 通过 __atttribute__ 扩展的 format 属性,用来指定变参函数的参数格式检查. 它的使用方法如下: __attribute__(( forma ...

  5. 树莓3B+_apt-get update && apt-get upgrade

    在Windows下安装软件,我们只需要有EXE文件,然后双击,下一步直接OK就可以了.但在LINUX下,不是这样的.每个LINUX的发行版,都会维护一个自己的软件仓库,我们常用的几乎所有软件都在这里面 ...

  6. django中models的filter过滤方法

    __gt     大于__gte   大于等于 __lt      小于 __lte    小于等于 __in     存在于一个list范围内 __startswith    以...开头 __is ...

  7. Oracle Data Provider for .NET Support for Microsoft .NET Core

    Oracle Data Provider for .NET Support for Microsoft .NET Core的官方地址,记录下来,按照官方描述,会在2017年底左右发布,暂时还没有看到相 ...

  8. 20155318 《Java程序设计》实验三 (敏捷开发与XP实践)实验报告

    20155318 <Java程序设计>实验三 (敏捷开发与XP实践)实验报告 实验内容 XP基础 XP核心实践 相关工具 实验步骤 (一)敏捷开发与XP 软件工程是把系统的.有序的.可量化 ...

  9. 2016-2017-2 20155339《 java面向对象程序设计》实验四Android程序设计

    2016-2017-2 20155339< java面向对象程序设计>实验四Android程序设计 实验内容 1.Android Stuidio的安装测试: 参考<Java和Andr ...

  10. iOS 的音频播放

    一.Audio Toolbox 1.使用代码 #import <AudioToolbox/AudioToolbox.h> AudioServicesPlaySystemSound(1106 ...