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

NetworkX 最短路径算法选型图

目标:一步一步用 Dijkstra 解一个具体例子

我们用一个非常小的有向带权图,源点为 A,权重均为正数。图(邻接表形式):

  • A → B (6)
  • A → D (1)
  • D → B (2)
  • B → C (5)
  • D → E (1)
  • E → C (5)

节点集合:A, B, C, D, E。目标是从 A 找到到每个点的最短距离,并展示如何一步步更新 distprev(用于重建路径)。


初始化

  • dist[A] = 0(源点)
  • 其它 dist = ∞dist[B]=∞, dist[C]=∞, dist[D]=∞, dist[E]=∞
  • prev 全为 Noneprev[A]=prev[B]=prev[C]=prev[D]=prev[E]=None
  • 最小堆(或优先队列)初始放入 (0, A)

我会把堆写成有序列表表示(最小在左):例如 [(0,A)]


步骤 0 — 初始状态

  • 堆: [(0, A)]
  • dist: A:0, B:∞, C:∞, D:∞, E:∞
  • prev: all None

弹出 1:取出 A(dist=0)

弹出堆得到 u = A, d_u = 0。现在松弛 A 的邻边:

  1. 松弛边 A→B (6):

    • 计算通过 A 到 B 的距离:d_u + w = 0 + 6 = 6。(逐位算:0 + 6 = 6)

    • dist[B] = ∞,6 < ∞,所以更新:

      • dist[B] = 6
      • prev[B] = A
      • (6, B) 推入堆
  2. 松弛边 A→D (1):

    • 计算 0 + 1 = 1

    • dist[D] = ∞,1 < ∞,更新:

      • dist[D] = 1
      • prev[D] = A
      • 推入 (1, D) 到堆

更新后:

  • 堆(可能无序显示,但最小优先): [(1, D), (6, B)]
  • dist: A:0, B:6, C:∞, D:1, E:∞
  • prev: A:None, B:A, C:None, D:A, E:None

弹出 2:取出 D(dist=1)

弹出 u = D, d_u = 1。松弛 D 的邻边:

  1. 松弛 D→B (2):

    • 通过 D 到 B 的候选距离:d_u + w = 1 + 2 = 3。(逐位算:1 + 2 = 3)

    • 现在 dist[B] = 6,3 < 6,所以更新:

      • dist[B] = 3
      • prev[B] = D
      • 推入 (3, B) 到堆
  2. 松弛 D→E (1):

    • 计算 1 + 1 = 2

    • dist[E] = ∞,2 < ∞,更新:

      • dist[E] = 2
      • prev[E] = D
      • 推入 (2, E) 到堆

更新后:

  • 堆(含旧的过时条目 (6, B) 仍在堆里):[(2, E), (6, B), (3, B)](实际堆内部顺序可能不同,但最小是 (2,E)
  • dist: A:0, B:3, C:∞, D:1, E:2
  • prev: A:None, B:D, C:None, D:A, E:D

弹出 3:取出 E(dist=2)

弹出 u = E, d_u = 2。松弛 E 的邻边:

  1. 松弛 E→C (5):

    • 通过 E 到 C 的候选距离:d_u + w = 2 + 5 = 7。(逐位算:2 + 5 = 7)

    • dist[C] = ∞,7 < ∞,更新:

      • dist[C] = 7
      • prev[C] = E
      • 推入 (7, C) 到堆

更新后:

  • 堆:[(3, B), (6, B), (7, C)](6,B) 仍旧为过时条目)
  • dist: A:0, B:3, C:7, D:1, E:2
  • prev: A:None, B:D, C:E, D:A, E:D

弹出 4:取出 B(dist=3)

弹出堆,得到最小 (3, B)(这是最新有效的 B 条目,因为 3 == dist[B])。松弛 B 的邻边:

  1. 松弛 B→C (5):

    • 候选距离:d_u + w = 3 + 5 = 8。(逐位算:3 + 5 = 8)
    • 现在 dist[C] = 7,8 < 7 不成立,因此不更新 C。(我们发现通过 B 到 C 的路径比已知的通过 E 的路径更长)

因此没有更新。

更新后堆仍有 (6, B)(过时)和 (7, C)

  • 堆:[(6, B), (7, C)]
  • dist 不变:A:0, B:3, C:7, D:1, E:2
  • prev 不变。

弹出 5:弹出过时的 B 条目 (6, B)

弹出 (6, B),但检查发现 dist[B] = 3 与条目距离 6 不一致,说明这是之前的过时条目,直接跳过,不做松弛。

堆现在: [(7, C)]


弹出 6:取出 C(dist=7)

弹出 (7, C),这是 C 的有效最小条目(7 == dist[C])。松弛 C 的邻边:在我们图中 C 没有出边(或没有更有用的出边),所以没有任何更新。

堆为空,算法结束。


最终结果

  • 最短距离 dist

    • A: 0
    • B: 3 —— 路径 A → D → B,计算:A→D (1) + D→B (2) = 3
    • C: 7 —— 路径 A → D → E → C,计算:A→D (1) + D→E (1) + E→C (5) = 7
    • D: 1 —— 路径 A → D
    • E: 2 —— 路径 A → D → E
  • prev 指向(用于重建):

    • prev[A] = None
    • prev[D] = A
    • prev[B] = D
    • prev[E] = D
    • prev[C] = E

路径重建举例(从 prev 回溯)

重建到 C 的路径:
C <- prev[C]=E <- prev[E]=D <- prev[D]=A,逆序得到:A → D → E → C

重建到 B 的路径:
B <- D <- A,逆序:A → D → B


简短 Python 验证(你可以复制运行)

import heapq
INF = float('inf')graph = {'A': [('B', 6), ('D', 1)],'B': [('C', 5)],'C': [],'D': [('B', 2), ('E', 1)],'E': [('C', 5)],
}def dijkstra(graph, src):dist = {v: INF for v in graph}prev = {v: None for v in graph}dist[src] = 0heap = [(0, src)]while heap:d,u = heapq.heappop(heap)if d != dist[u]:continuefor v,w in graph[u]:if dist[v] > d + w:dist[v] = d + wprev[v] = uheapq.heappush(heap, (dist[v], v))return dist, prevdist, prev = dijkstra(graph, 'A')
print('dist =', dist)
print('prev =', prev)
# 重建 C 的路径
path = []
cur = 'C'
while cur:path.append(cur)cur = prev[cur]
path.reverse()
print('A -> C path:', path)  # ['A','D','E','C']

运行后你会看到:

dist = {'A': 0, 'B': 3, 'C': 7, 'D': 1, 'E': 2}
prev = {'A': None, 'B': 'D', 'C': 'E', 'D': 'A', 'E': 'D'}
A -> C path: ['A', 'D', 'E', 'C']


✅ 一、使用 networkx 自带的 Dijkstra 函数

networkx 是 Python 最常用的图算法库,它内置了 Dijkstra 算法及其变体。

安装:

pip install networkx

💡 示例:最简单的用法

import networkx as nx# 创建一个有向带权图
G = nx.DiGraph()# 添加带权边 (起点, 终点, 权重)
G.add_weighted_edges_from([('A', 'B', 6),('A', 'D', 1),('D', 'B', 2),('B', 'C', 5),('D', 'E', 1),('E', 'C', 5)
])# 1️⃣ 计算从 A 到所有点的最短距离
lengths = nx.single_source_dijkstra_path_length(G, 'A')
print("最短距离:", lengths)# 2️⃣ 计算从 A 到所有点的最短路径
paths = nx.single_source_dijkstra_path(G, 'A')
print("最短路径:", paths)# 3️⃣ 只算从 A 到 C 的最短路径
path = nx.dijkstra_path(G, 'A', 'C')
length = nx.dijkstra_path_length(G, 'A', 'C')print("A 到 C 的最短路径:", path)
print("A 到 C 的距离:", length)

输出结果:

最短距离: {'A': 0, 'D': 1, 'B': 3, 'E': 2, 'C': 7}
最短路径: {'A': ['A'], 'D': ['A', 'D'], 'B': ['A', 'D', 'B'], 'E': ['A', 'D', 'E'], 'C': ['A', 'D', 'E', 'C']}
A 到 C 的最短路径: ['A', 'D', 'E', 'C']
A 到 C 的距离: 7

✅ 二、networkx 中 Dijkstra 的相关函数速查表

函数名作用备注
nx.dijkstra_path(G, source, target, weight='weight')求最短路径(单源单目标)返回路径节点列表
nx.dijkstra_path_length(G, source, target, weight='weight')求最短路径的距离返回距离(数字)
nx.single_source_dijkstra(G, source, target=None, weight='weight')同时返回最短路径和距离若指定 target 只算一个
nx.single_source_dijkstra_path(G, source)求从源点到所有点的最短路径返回 {节点: 路径}
nx.single_source_dijkstra_path_length(G, source)求从源点到所有点的最短距离返回 {节点: 距离}
nx.multi_source_dijkstra(G, sources, target=None)多源最短路径可设多个起点
nx.all_pairs_dijkstra(G, weight='weight')全源最短路径返回所有对的最短路径
nx.all_pairs_dijkstra_path_length(G)全源最短距离返回 {源点: {终点: 距离}}

✅ 三、如果你的图用邻接矩阵表示

也可以用 scipy 的 Dijkstra:

from scipy.sparse.csgraph import dijkstra
from scipy.sparse import csr_matrix# 邻接矩阵(∞用0或np.inf)
import numpy as np
graph = np.array([[0, 6, 0, 1, 0],[0, 0, 5, 0, 0],[0, 0, 0, 0, 0],[0, 2, 0, 0, 1],[0, 0, 5, 0, 0]
])# csr_matrix 稀疏矩阵
g = csr_matrix(graph)# 从节点0 (A) 出发
dist, predecessors = dijkstra(csgraph=g, directed=True, indices=0, return_predecessors=True)print("最短距离:", dist)
print("前驱节点索引:", predecessors)

输出结果类似:

最短距离: [0. 3. 7. 1. 2.]
前驱节点索引: [-9999    3    4    0    3]

(索引 0=A, 1=B, 2=C, 3=D, 4=E)


✅ 四、小结

需求推荐函数所属库
简单地求最短路径(图结构)nx.dijkstra_pathnetworkx
求所有节点的最短距离nx.single_source_dijkstra_path_lengthnetworkx
用矩阵形式计算scipy.sparse.csgraph.dijkstrascipy
只想学习算法逻辑手写版本(前面我们写的)无需库


🧭 一、最常用的最短路径算法总览

算法主要函数支持负权适用场景备注
Dijkstranx.dijkstra_path / nx.single_source_dijkstra❌ 不支持边权非负的图(稀疏或中等)默认权重字段为 "weight"
Bellman-Fordnx.bellman_ford_path / nx.single_source_bellman_ford✅ 支持存在负权边但无负权环比 Dijkstra 慢
Floyd–Warshallnx.floyd_warshall / nx.floyd_warshall_predecessor_and_distance✅ 支持计算所有点对最短路径(全源)适合小规模图
Johnsonnx.johnson✅ 支持计算所有点对最短路径(可含负权边)对稀疏图效率高
BFS(广度优先搜索)nx.shortest_path(未指定权重)无权图(或权重全相等)O(V+E),最简单最快
A* (A-star)nx.astar_path / nx.astar_path_length❌ 不支持有启发函数的单源单目标搜索常用于地图最短路
Yen’s K-Shortest Pathsnx.shortest_simple_paths❌ 通常非负权求前 K 条最短简单路径比较高级的算法

🧩 二、每类算法的常用函数详细说明

1️⃣ Dijkstra(迪杰斯特拉)

最常用、最经典、权重非负时最快。

nx.dijkstra_path(G, source, target, weight='weight')
nx.dijkstra_path_length(G, source, target, weight='weight')
nx.single_source_dijkstra(G, source)
nx.all_pairs_dijkstra(G)

2️⃣ Bellman–Ford(贝尔曼–福德)

支持负权边(只要没有负权环)。

nx.bellman_ford_path(G, source, target, weight='weight')
nx.single_source_bellman_ford(G, source)
nx.bellman_ford_predecessor_and_distance(G, source)

3️⃣ Floyd–Warshall(弗洛伊德–沃肖尔)

适合求所有节点对之间的最短路径。

nx.floyd_warshall(G, weight='weight')  # 返回距离矩阵字典
nx.floyd_warshall_predecessor_and_distance(G, weight='weight')

4️⃣ Johnson(约翰逊算法)

也是全源最短路径算法,但对稀疏图(边远小于节点平方)更高效。

nx.johnson(G, weight='weight')

5️⃣ BFS(Breadth-First Search)

权重全为 1 或未加权时的最短路径算法。

nx.shortest_path(G, source, target)
nx.shortest_path_length(G, source, target)
nx.single_source_shortest_path(G, source)
nx.all_pairs_shortest_path(G)

💡 小技巧:
如果你的图有权重但全为 1,可以用 nx.single_source_shortest_path_length(G, source)


6️⃣ A* (A-Star)

单源单目标,常用于带启发函数的路径搜索(例如地图、游戏寻路)。

nx.astar_path(G, source, target, heuristic=None, weight='weight')
nx.astar_path_length(G, source, target, heuristic=None, weight='weight')

示例(假设坐标启发):

def heuristic(u, v):(x1, y1) = pos[u](x2, y2) = pos[v]return ((x1-x2)**2 + (y1-y2)**2)**0.5
nx.astar_path(G, 'A', 'Z', heuristic=heuristic)

7️⃣ 多条最短路径(K-Shortest Paths)

如果你想求多条不重复的最短路径:

paths = list(nx.shortest_simple_paths(G, source, target, weight='weight'))
print(paths[:3])  # 前3条最短路径

📦 三、全源最短路径相关函数总览

函数算法类型支持负权返回内容
nx.all_pairs_dijkstra_pathDijkstra各节点对路径
nx.all_pairs_dijkstra_path_lengthDijkstra各节点对距离
nx.floyd_warshallFloyd-Warshall各节点对距离
nx.johnsonJohnson各节点对距离

🧠 四、如何选算法?

场景推荐算法
无权图(边权=1)BFS (nx.shortest_path)
边权非负Dijkstra (nx.single_source_dijkstra)
含负权但无负环Bellman–Ford 或 Johnson
所有节点对最短路(全源)Floyd–Warshall(小图) / Johnson(大图)
地图或启发式搜索A*
想要多条路径shortest_simple_paths

💡 示例对比

import networkx as nxG = nx.DiGraph()
G.add_weighted_edges_from([('A','B',1), ('B','C',2), ('A','C',5), ('C','D',1)
])# Dijkstra
print("Dijkstra:", nx.dijkstra_path(G, 'A', 'D'))# Bellman–Ford
print("Bellman–Ford:", nx.bellman_ford_path(G, 'A', 'D'))# Floyd–Warshall
print("Floyd–Warshall 距离矩阵:", nx.floyd_warshall(G))# 全源 Johnson
print("Johnson 全源:", nx.johnson(G))

http://www.dtcms.com/a/515390.html

相关文章:

  • 互联网公司软件开发全流程规范文档
  • springboot基于JAVA的二手书籍交易系统的设计与实现(代码+数据库+LW)
  • STM32产品程序测试完整指南
  • AWS Data Exchange:概述、功能与安全性
  • 掌控制造脉络:电子元器件行业常用ERP系统概览与参考指南
  • 个人网站静态网页模板海淀区网站建设
  • 低代码开发的约束性及ABP框架的实践解析
  • centos7部署jenkins
  • 软文街官方网站如何提升做网站的效率
  • 【论文精读】GenTron:基于 Transformer 的扩散模型革新图像与视频生成
  • 【RabbitMQ】简介
  • (done) 自学 MPI (4) Environment Management Routines
  • webrtc弱网-RembThrottler类源码分析及算法原理
  • 鸿蒙的声明式 UI转换为 JSView
  • 【MySQL】从零开始了解数据库开发 --- 如何理解事务隔离性
  • BugKu Web渗透之 never_give_up
  • Cangjie语言核心技术深度解析测评:迈进鸿蒙原生开发的安全新时代!
  • 长乐市住房和城乡建设局网站网站开发公司的推广费用
  • 绵阳住房和城乡建设部网站网站页面优化怎么做
  • 关于git的使用(在gitee和gitcode上部署自己的项目流程)
  • PHP 字符串操作详解
  • 3合一网站怎么做免费数据统计网站
  • 17.基础IO_3
  • Ubuntu 系统掉电导致分区损坏无法启动修复方案
  • 相机模组,模组是什么意思?
  • 申威架构ky10安装php-7.2.10.rpm详细步骤(国产麒麟系统64位)
  • STM32F407 通用定时器
  • lodash-es
  • 股票交易网站建设四会市城乡规划建设局网站
  • API技术深度解析:从基础原理到最佳实践