C++图论之常规最短路径算法的花式玩法(Floyd、Bellman、SPFA、Dijkstra算法合集)

1. 前言

权重图中的最短路径有两种,多源最短路径和单源最短路径。多源指任意点之间的最短路径。单源最短路径为求解从某一点出到到任意点之间的最短路径。多源、单源本质是相通的,可统称为图论的最短路径算法,最短路径算法较多:

  • Floyd-Warshall算法。也称为插点法,是一种利用动态规划思想寻找权重图中多源点之间[最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授[罗伯特·弗洛伊德命名。
  • Bellman_ford算法。贝尔曼-福特算法取自于创始人理查德.贝尔曼和莱斯特.福特,暴力穷举法,算法效率较低。但是,能解决的问题范围较大,如负权问题。
  • SPFA算法。Bellman-Ford的队列优化版,本质一样。
  • Dijkstra算法。迪杰斯特拉算法(Diikstra) 是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。经典算法,本质是贪心算法。

下面逐一介绍这三种算法。

2. Floyd-Warshall

权重图中,任意两点之间的路径可能存在多条,但是最短的是哪条?

如果你现在想从城市1城市2,固然是想找一条最短路径的,找出量短路径,意味着省时、省钱、省精力……

本质上就是做选择题。面对这样的情况,你首先要做的便是绘制地图,描绘出与城市1城市2直接、间接相邻的城市。并初始化城市与城市之间的已知权重。如下图所示。

1.png

先得到两点直接相连时的路径。如上图的12两点之间,不存在直接相连的边,初始可认为是很大、很大……

使用矩阵描述为 graph[1][2]=INFINF是一个自定义的常量,存储一个较大的值。

现实生活中,当直接不能到达,或直接到达的成本很高时,会考虑经过一个中转站。这样可能会降低成本。到底经过那个中转站能降低成本。这个只能逐一试试。可以把除了12之外的所有节点做为中转站,然后比较是否比之前的路径更短。比如,在12之间插入3号节点。

这样你的旅行路就分割成了两段,一段是从13、一段是从32。如下图,标注红色的为新路线。1->3的权重为43->2的权重为7。累加权重为11。显然,比之前的INF要短的,想必你是不会犹豫地选择这条新路线。

这时在你的脑海中,应该使用如下的式子获取到1->2的新的权重。

//因为走 1->3后、再走`3->2`的路线要比之前路径短
//    必然要选择新的路线
if(graph[1][3]+graph[3][2]<graph[1][2])
    graph[1][2]=graph[1][3]+graph[3][2];

2.png

原理很简单,这时你应该会思考,可能经过2个或多个或其它中转站比经过1个中转站更省成本。是的,现在还不是最终的选择。

每一次更新后,你需要继续试着添加其它节点做为中转站。检查是否更短,如果更短,继续更新,如果更远,就不用更新。如可以试着把4号点做为中转站。这时路线权重变成 graph[1][4]+graph[4][2]=15,并没有比之前经3为中转站中的值更小,以4为中转站这个方案要淘汰掉。

选择5做为中转站,你会发现graph[1][5]+graph[5][2]=11。既然发现了更短的路径,更新邻接矩阵中graph[1][2]的值。这时你应该有所感悟,下图中的邻接矩阵不就是一张动态规划表吗?

**Tips:**在不断的插入节点,得到新路线后,节点之间的权重值会发生变化。如果需要保留原始图中的节点之间的信息,可以再创建独立的动态规划表。

3.png

1和2之间把其它节点都插入了,然后得到了现阶段的最短权得和10。是不是就是最终的结果呢?

如果你善于观察,从1->3、然后3->5、再5->2,其权重和为7。这条路径才是1->2之间的最短路径。也就是说,经过多个中转站也许比只经过一个中转站会让路径更短。

现在的问题是,我们直接朝目标而来,其实没有考虑,你所经过的中间路径也有可能有更短的。如现阶段,我们认为的1->5然后5->2是最短的,但是,是否忽视了1->5也许也存在最短路径。只有最短加最短才能得到真正的最短。

其实,这也符合动态规划思想,必须存在最优子结构吗!最终问题必然是前面的的子问题一步一步推导出来的。所以,Floyd算法告诉我们,必须更新任意两点之间的路径,才能得到你希望的两点之间的最短路径。

也就是说,当分别插入1、2、3、4、5号节点时,对其它任意两点的距离影响都要记录在案。比如,在插入1号节点时,对其它任意两点的影响如下图所示。对3-44-5之间的影响是较大。

4.png

选择3号点做作插入点,检查其它任意两点之间经过3号点是否能让路线变得更短。发现,1-5之间的距离被缩短了。

5.png

当选择5号点做为插入点,计算1-2点间的距离时,此时会发现1->3->5->2才是最终的最短距离。

理解Floyd算法的关键,动态规划本质是穷举算法,只有当所有可能都计算一次,才能得到最终结果。

编码实现:

#include <iostream>
using namespace std;
//图
int gra
  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Dijkstra算法 Dijkstra算法的思路是:设有向图G=(V,E),其中,V={v0,v1,…,vn-1},cost[i][j]表示有向边的权值。若不存在有向边,则cost[i][j]的权为无穷大(∞)。数组ds记录从源点到其他各顶点当前的最短距离,其初值ds[i]=cost[v][i],从s之外的顶点集合V-S中选一个顶点vu,使ds[u]的值最小。于是从源点v到达只通过s中的顶点,把u加入集合s中调整ds中的记录从源点到V-S中每个顶点vj的距离:从原来的ds[j]和ds[u]+cost[u][j]中选择较小的值作为新的ds[j]。重复上述过程,直到s中包含其余各顶点的最短路径 Floyd-warshall算法 Floyd-warshall算法的基本思想是:如果从vi到vj有边,则从vi到vj存在一条长度为cost[i][j]的路径。该路径不一定是最短路径,尚需要进行n次试探。首先考虑路径(vi,v0, vj)是否存在。如果存在,则比较其路径长度。取长度较短者为从vi到vj的中间顶点的序号不大于0的最短路径。假如在路径上再增加一个顶点v1,即如果(vi,…, v1)和(v1,…,v)分别是当前找到的中间顶点的序号不大于0的最短路径,那么,(vi,…, v1,…, vj)就有可能是从vi到vj中间顶点的序号不大于1的最短路径。将它和已经得到的从vi到vj中间顶点的序号不大于0的最短路径相比较,从中选出中间顶点的序号不大于1的最短路径后,在增加一个顶点v2,继续进行试探。依次类推,直至经过n次比较,最后求得的必是从vi到vj的最短路径。按此方法,可以同时求得各对顶点间的对段距离。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一枚大果壳

码文不易,晚上熬夜需要点咖啡钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值