当前位置: 首页 > news >正文

最短路径问题(图论)

1 Floyd

作用:

求图中所有顶点之间的最短路径,包括有向图或者无向图,权重正负皆可,用来一次性求所有点之间的最短路径。

原理

  • 递推公式,F[K][X][Y] 表示顶点x,y中间最多只经过(0,1,2,…k)这些顶点时的最短路径
    在这里插入图片描述

  • 三层遍历,第一层必须为中间层,这样才会逐步将中间层扩大,如果中间层没有放在第一层,会导致 每个f[x] 实际只会求取一次,结果不正确。

  • **根据上面实际上需要使用三位数组进行求解,但是可以优化为二维数组,初始状态时,时候F[X][Y]可能为 “0/实际权重/正无穷”;如果点X,Y直连,则F[X][Y]为实际权重;如果点X==Y,则F[X][Y]=0;如果点X,Y非直连,则F[X][Y] = 正无穷

实际的代码如下:

for k in range(1, n + 1):for x in range(1, n + 1):for y in range(1, n + 1):f[k][x][y] = min(f[k - 1][x][y], f[k - 1][x][k] + f[k - 1][k][y])

最外层的数组可以省略,优化为如下:

for k in range(1, n + 1):for x in range(1, n + 1):for y in range(1, n + 1):f[x][y] = min(f[x][y], f[x][k] + f[k][y])

详见 Floyd算法解析

2 Dijsktra算法

作用

是一种单源最短路径算法,他能找到其中一个点到其他所有点的最短路径,只支持权重为正的情况, 因为Dijkstra算法存在贪心的策略,一旦到某个节点的最短路径被确定,后续就无法修改。故无法支持权重为负的情况。

实现原理

假设有如下图结构,求节点A到节点F的最短距离,实现过程如下:

  • 首先准备一个优先队列,一个最短距离前驱表t1,以及一个节点最短距离是否已经被计算出的表visisted。t1[v]中包含顶点A距离顶点v的最短距离以及顶点v的前驱节点,初始的时候,将顶点A到它自身的距离为0,其他顶点到顶点A的距离为无穷大,前驱节点为空,将(0,A)放到优先队列中(以第一位距离作为排序的key,小根堆)
    在这里插入图片描述
  • 首先从优先队列中取出首位距离最短的节点信息(0, A),然后依次遍历顶点A的所有边(B,C),将(4, B),(5,C)放到优先队列中,同时更新最短距离前驱表,并将顶点A放到visited表,表示下次遍历到顶点A的时候,直接忽略,具体结果如下:
    在这里插入图片描述
  • 然后再从优先队列中取出顶点(4, B)信息,依次遍历B所有的边(C, D,E)其中A已经被放到visited中,无需再遍历,遍历B的边C的时候,发现权重之和(A->B + B->C =4 + 11 < 5)大于当前表中已经存在的距离,故这里不更新C的距离以及前驱;遍历边D的时候,发现(A->B + B-> = 4 + 9 = 13 < 正无穷 )小于当前表中已经存在,更细表中顶点D到A的最短距离为13以及前驱节点为B。
  • 后续的操作与上面一样,直到优先队列中的元素被取完,如果从优先队列中取出的顶点到A的距离小于表中已经存在的距离或者已经处于visited表中,则直接忽略即可
  • 最后通过得到的最短距离前驱表可以得到顶点A到其他所有节点的最短距离以及对应的路径

备注:最短距离前驱表中前驱信息是用来根据回溯得到目标顶点的最短路径
代码实现

def dijkstra(n, source, weights):## weights 列表的每一个元素格式为(n1, n2, w)表示顶点n1->n2的权重为w## n 表示顶点的格式,source表示原顶点## 这里需要将顶点信息都转化为0,1... n-1的编号# 为求顶点source 到其他顶点的最短距离neighbors = [[] for _ in range(n)]inf = float('inf')edges = [[inf for _ in range(n)] for _ in range(n)]for v1, v2, w in weights:neighbors[v1].append(v2)neighbors[v2].append(v1)edges[v1][v2] = wedges[v2][v1] = wpq = []heapq.heappush(pq, (0, source))visited = set()t1 = {}for i in range(n):t1[i] = (inf, -1)t1[source] = (0, -1)print(f"neighbors:{neighbors}")while pq:print(f"pq:{pq}")d, v = heapq.heappop(pq)if d > t1[v][0]:continuevisited.add(v)for p in neighbors[v]:d1 = d + edges[v][p]print(f"v:{v} p:{p} d:{d} edges[v][p]:{edges[v][p]}")if p not in visited and (d1 < t1[p][0]):t1[p] = (d1, v)heapq.heappush(pq, (d1, p))return t1

Bellman-Ford算法

作用

上面的dijkstra算法只支持权重为正场景,对于有些权值为负的场景需要使用BellmanFord算法,该算法也是求单个顶点到到其余顶点的最短路径,如果存在负环路径,则不存在最短路径

实现原理

假设有如下图结构,求节点A到其他节点的最短距离,实现过程如下
在这里插入图片描述

  • 首先需要准备一个最短路径前驱表t1, t1[v] 中包含顶点v到顶点A的最短距离以及顶点v的前驱节点。初始的时候 t1[‘A’] = (0, -1) 其他的t[v] = (正无穷,-1)
  • 然后遍历所有的边,每遍历一个边(v1, v2)的时候,如果当前节点A到v1的t1[v1][0] + 当前边的权重w(v1->v2)小于当前t1[v2][0],则更新v2的最短距离以及前驱节点
    t1[v1][0] + w(v1->v2) < t1[v2][0]
  • 上面的步骤最多执行n - 1次,如果中间有一次 没有节点的最短距离需要更新,则说明所有A到所有节点的最短距离已经全部收敛,直接返回。
  • 如果上面执行n- 1次,每次均存在节点的最短距离需要更新,则图中可能存在负权环,所谓负权环就是如下图所示,随着遍历次数的增加,A到C的距离只会越来越小-2, -3, -4等等,此时说明最短路径,那么只需要再遍历第n次,看看是否仍存在某个节点的最短路径被更新,如果被更新,则该节点存在负权环。
    在这里插入图片描述
http://www.dtcms.com/a/344759.html

相关文章:

  • 中国SM系列密码算法的入门教程
  • 网络实践——Socket编程UDP
  • Seaborn数据可视化实战:Seaborn颜色与样式定制教程
  • elasticsearch的使用
  • odoo-065 两个视图中的action类型的button互相引用,造成死循环
  • ubuntu使用fstab挂载USB设备(移动硬盘)
  • Claude Code接入Serena mcp
  • ESP32C5,使用espidf框架配置wifi扫描时报错,为什么会提示,ghz_5_channels的参数无效呢
  • 开发避坑指南(32):FastJSON异常JSONArray cannot be cast to JSONObject解决方案
  • 什么是数据分类分级?数据分类分级技术实现路径及产品推荐
  • ​Kubernetes 详解:云原生时代的容器编排与管理
  • 08.21总结
  • 【yocto】BitBake指令汇总解析
  • 基于springboot的农产品社区配送系统
  • 线性回归的学习
  • C++ unistd.h库文件介绍(文件与目录操作, 进程管理, 系统环境访问, 底层I/O操作, 系统休眠/执行控制)
  • golang 非error错误分类
  • 【如何生成专业级 API 接口文档:从规范到实战】
  • 指针实现数组的逆序存放并输出
  • IKE 与 ISAKMP 核心笔记
  • JCTools Spmc 单生产者-多消费者的无锁并发有界队列
  • 支持轻量化部署的混元3D世界模型Lite版本上线魔乐社区,昇腾部署实践来啦
  • FCT/ATE/ICT通用测试上位机软件
  • Leetcode—595. 大的国家【简单】
  • JUC之Fork/Join
  • WindowsAPI|每天了解几个winAPI接口之网络配置相关文档Iphlpapi.h详细分析9
  • 2-3.Python 编码基础 - 类型检测与类型转换
  • Vue 实现可拖拽分割布局(支持左右、上下拖拽调整)
  • Java 学习笔记(基础篇7)
  • 2025年游戏盾SDK动态加密技术全景解析:从防御破解到重塑游戏安全基石