最短路问题
最短路问题
图论中求某点到某点最短的路径长度。
图中点1到点4的最短路径长度应为3.
分类
最短路问题分为两类:单源最短路和多源最短路。前者只需要求一个固定的起点到各个顶点的最短路径,后者则要求得出任意两个顶点之间的最短路径。
单源最短路
Dijkstra算法
Dij基于一种贪心的思想,我们假定有一张没有负边的图。首先,起点到起点的距离为0,这是没有疑问的。现在我们对起点和它能直接到达的所有点进行松弛。
因为没有负边,这时我们可以肯定,离起点最近的那个顶点的dist一定已经是最终结果。为什么?因为没有负边,所以不可能经由其他点,使起点到该点的距离变得更短。
那现在我们来考察2号点:
我们对2号点和它能到达的点进行松弛。这时dist保存的是起点直接到达或经由2号点到达每个点的最短距离。我们这时候取出未访问过的dist最小的点(即4号点),这个点的dist也不可能变得更短了(因为其他路径都至少要从起点直接到达、或者经由2号点到达另一个点,再从这另一个点到达4号点)。
继续这个流程,松弛4号点能到达的点:
然后分别考察3、5号点,直到所有点都被访问过即可。
总结一下,
如何取出离顶点最近的点?如果暴力寻找,那就是朴素的
bellman_ford算法
- bellman_ford算法专门处理可能存在负环的有限路线单源点最短路问题
- 枚举所有的点,能松弛就进行松弛操作,直到所有点都不能松弛了。
- 能通过k次迭代求出从源点到终点不超过k条边构成的最短路的路径,一般情况下,要求途中不存在负环。但是在边数有限制的情况下允许存在负环。
bellman_ford的队列优化——spfa
- 每一次松弛的时候bellman_ford都要枚举所有的点,而其实有很多点都是不需要枚举的,所有有很多的无效枚举
- 考虑每次松弛的时候只需要枚举与上次被松弛的点相连的点就可以了
spfa算法的实现 - dis[i]表示i号点到源点的最短距离,dis[1]=0,其他初始化为INF.
- 维护一个队列,里面放所有需要迭代的点。初始时队列只有源点。同时用一个布尔数组st[i]记录每个点是否在队列中。
- 每次迭代取出队头,去更新队头能到达的点,如果能更新并且更新的点不在队列中,则放入队列。一直迭代下去直到队列为空(即源点到所有节点的最短距离都确定),就结束。
spfa判断是否有负环 - 维护一个cnt[i]数组,表示第i号点到源点最短路的边数。
- 如果有一个点的cnt[i]>n,则说明图中至少有一个点经过了两次,也就说明存在负环。
- 需要注意的是,用spfa判断负环,这个负环不一定在源点的后继节点上,因为一开始要将所有节点都入队。
spfa写法注意 - spfa写法在形式上虽然和bfs类似,但是bfs中一个点出了队列之后就不可能重新进入队列,但spfa中一个点出队列之后可能再次被放入队列,也就是说一个点更新过其他点之后,过了一段时间可能本身被改进,于是再次用来改进其他的点,这样反复迭代下去。
为什么比bellman_ford算法更优化 - 在bellman_ford算法中,若某个点的最短路径被更新后,那么我们必须堆所有边的终点再做一次松弛操作;但是在spfa中,若某个点的最短路径被更新,只更新与该点相邻的点。效率更高。
spfa求最短路
spfa判断负环
Floyd
dp太菜,floyd不太会
Floyd带图解详解
总结
Dijkstra-朴素
1.初始化距离数组dis[1]=0,dis[i]=INF;
2.for n次循环,每次循环确定一个min加入S集合,n次之后就得出所有的最短距离
3.将不在S中dis_min的点->t
4.t->S加入最短路集合
5.用t更新到其他点的距离
Dijkstra-堆优化
1.利用邻接表,优先队列
2.在priority_queue<PII, vector<PII>, greater<PII>> q;
将返回堆顶
3.利用堆顶来更新其他点,并在堆中加入类似bfs
Bellman_ford
1.注意需要备份数组
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具