【图论】单源最短路径问题——三种算法比较(Dijkstra,Bellman_ford,SPFA)

算法名称能否处理负权边时间复杂性
Dijkstra

因为核心算法是贪心,

所以不能处理负边权

o(V^2)

o(VlogV)Heap+邻接表

Bellman—Ford能,只要不是负环即可o(VE)邻接表
SPFA

能,本质是队列优化的

Bellman—Ford,

负环只会增加出入队列次数

o(km)~o(VE)

k为常数,通常不超过2

  • Dijkstra

每次确定了到一个点的最短距离(当前收录),再用该点更新到其它点的距离。

保证路径是按照递增(非递减)顺序生成

//1:后收录的顶点不会影响先收录顶点的距离。(因为后收录的顶点距离一定比先收录的顶点距离大,只会增大路径长度)因此收录集中每个顶点的最短路都已确定。

//2:每次收录顶点只会影响其邻接点的距离。(因为此时未收录的邻接点{B}距离都比本次收录的A大,只会增加到该点的路径长度,不是最短路)

因为需要路径按照非递减顺序生成,因此如果存在不与源点直接连通的负边情况,且负边权足够小,则可能让之前不够短的路径变最短,之前的收录就无效了,需要重新计算。

  • Bellman-Ford

每次对所有边松弛(试探每条边对其邻接边的影响,减短则更新),重复更新N-1轮(防止后更新的边对先更新边的影响)。

若某轮操作后,没有边被更新,说明数组d中所有值都已达到最优,

提前退出可以稍微加快一点速度

伪代码: 

  

 具体实现:

最后再进行一轮操作,若某条边仍然能被更新,则说明图中有从源点可达的负环。

否则,则已达最优。 

若需要用Bellman-Ford算法统计最短路径条数,

由于每次对所有边进行操作,所以会反复累计前驱点,

因此存储前驱的数组最好使用set<int> pre[MAXN]

  • SPFA 

注意到每条边只可能影响其邻接边,不需要遍历所有边的邻接边,只需要遍历该边的邻接边即可,

因此使队列操作成为了可能。

每次将收到受到影响的邻接边入队,下一次轮到其出队看看其影响会不会传递到下一条邻接边中,

直到队列为空(不再存在需要传递的影响)

伪代码: 

具体实现:

 

 

 

如果某个顶点的入队次数超过N-1,说明图中存在从源点可达的负环

如果负环从源点不可达,则需要

  • 添加一个辅助顶点C
  • 添加一条从源点到达C的有向边
  • N-1条从C到达除源点外各顶点的有向边

才能判断负环是否存在

时间复杂度是O(kE),在很多情况下k不超过2,可见该算法在大部分数据时异常高效

经常性地优于堆优化的Dijkstra算法。

 但若图中有从源点可达的负环,传统SPFA的时间复杂度就会退化成O(VE)

上面代码中的FIFO队列,

可以替换成优先队列priority_queue(SLF优化),

或者双端队列deque(LLL优化)

效率至少提高50%

上面使用队列的是SPFA的BFS版本,

 若将队列替换成栈,则可以实现DFS版本的SPFA

对判环有奇效

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值