最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。" U) y' W, _9 [% k1 s( H) x
' N! p8 A4 T6 t: v( d: i# L
## 1. Dijkstra 算法+ k O& x" J; H& H
# P6 n5 ^" F' @* |, I4 O. ]Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。: }0 e4 L1 s3 B5 R' N# f
; \5 u8 b# \# x c5 A2 Z, F9 \
### 原理步骤$ I3 F- ^6 z7 K, k! K- i: q. `4 r9 L$ H
8 P0 L# W7 C9 [6 {# N
1. **初始化**:' ~0 j$ j2 V8 l9 y0 b A
- 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。6 \5 d' q& i5 D1 [; Y' P& \& ^
- 初始化一个空的优先队列,用于存储待处理的节点。+ w5 P/ s$ [2 z# e6 |9 ~3 F; l
1 c9 q. _1 x, P) Y7 f# z# y* L
2. **处理节点**: 8 o9 C& ?2 i+ X - 从优先队列中选取距离最小的节点作为当前节点。, U0 [- D, s7 f# F
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。 ! P. ?2 O0 J9 D, M9 C1 Z1 c! E: X: D1 C/ j' v, g$ ]
3. **重复处理**:! r: p$ J9 H6 D, h5 B9 @& n# p
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。 # n1 W( A( k( _, r. ?- B5 l6 U ; t( e$ K' p4 ]! U @0 c### 示例代码 " E0 S6 l/ v: M/ e) o0 l Z: J: E' r* r" e
```python . g3 R' S8 E& Z$ s' Ximport heapq! |1 Z. i M9 Q* M% z- N2 _
7 l7 o* S' b f, l5 T6 f ndef dijkstra(graph, start): 1 L2 V; g6 a9 b$ @ # 图的表示为字典,键为节点,值为邻接节点及其边权 + w" ?3 x+ T# W+ t+ Q queue = []1 Y& |" s% t$ F. H4 w
distances = {node: float('inf') for node in graph} : ?3 b4 T* ?: ^8 \9 @ distances[start] = 0 r0 r* d$ j% I0 V; [1 m- t heapq.heappush(queue, (0, start)) # (距离, 节点)& Z- `, t% c- s: M' I
K' G: q: t8 k& F9 H' b
while queue: 3 N1 V5 Y4 Y1 }% I& \' s" ^ current_distance, current_node = heapq.heappop(queue)* h( V' X7 x) |7 z! l0 V
: c- @2 B- n* T9 m) b. c7 Y9 Y # 只处理当前节点的最短距离/ ^3 Y3 q# p& M
if current_distance > distances[current_node]: 9 m5 y) n. m3 P. M# `/ n/ a. V continue ! E/ ~8 t4 W Q0 }! _+ w( l " O) g; I6 S( L! h for neighbor, weight in graph[current_node].items(): 5 U2 m) C; v) z1 W6 P distance = current_distance + weight 5 u$ T) D8 n0 [; ]9 K' k$ p8 F0 z: C
# 更新邻接节点的距离 % K7 f, S8 i5 w2 j* Q6 n, S if distance < distances[neighbor]:! ?7 V; o. C8 [ M) M
distances[neighbor] = distance , L$ I" O/ I& m, t2 N3 C3 X- z heapq.heappush(queue, (distance, neighbor)) & b, v+ B/ A! w9 ?, M4 S$ |" F+ U% L* [; O' @
return distances) Q& c/ Y) b3 d3 e# G" n- N* C' N
& r9 N( d4 f8 f, }# 示例图 ) H- Z, ?' v% ]graph = {" w/ m. u+ T2 X
'A': {'B': 1, 'C': 4}, ; b9 Z5 Z2 o2 @ ]* f 'B': {'A': 1, 'C': 2, 'D': 5},) s; R5 ?) T: t% b
'C': {'A': 4, 'B': 2, 'D': 1}, * Y* ]" u. M. u 'D': {'B': 5, 'C': 1}, ; e# C Z: Y6 `} / _, z! x( g/ A. ]7 O, f 2 W. m; ~3 z5 L0 d- a& {: astart_node = 'A' ! A6 K, R" k/ C, J' r) w: Dshortest_paths = dijkstra(graph, start_node) ! X0 v8 M# B5 z& E" {5 t( tprint(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4} * {+ T. i- Y4 o3 g``` ! Y) F4 `" y3 e1 L7 h' r+ _. E . f0 c& d' _9 A& p" `9 `- ~( a## 2. Bellman-Ford 算法 " S- @$ |& H# Z e! F* H$ y - v* V9 Y% \, bBellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。) [ L, X0 K# U
2 ]/ Z" E, d+ i: h
### 原理步骤1 C- b! L: Z0 L
6 U6 s5 q y4 l. t* A3 \/ W
1. **初始化**: Y' u9 K7 S9 a: }! T) S. T - 设置源节点到自己的距离为0,其他节点为无穷大。( V- V5 h l- _7 l/ Z% J: G
e$ ]4 `, r7 F n H* F
2. **松弛操作**: 6 q% T* q" m9 W" U; c: m - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。 - `' h1 \; J5 h* j2 R/ h: y2 U # n! Z* K/ t9 s. f' t3. **检查负权回路**:8 _2 Y* @1 l9 D8 K
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。 . G3 \' |* q8 W/ h1 Y: @9 t& N+ o. _2 R/ O z
### 示例代码 8 f. p, D8 F9 A ; Z i" P$ V7 r" ?$ x; Q```python ' {, Y. r. G* v5 R) w2 C1 i$ Odef bellman_ford(graph, start):& z# l5 c2 X" A4 Z' a2 v) I
# 初始化距离 1 f' c% J$ J5 A distances = {node: float('inf') for node in graph} ; ~& L7 K6 o' M" Q! t: ]0 f distances[start] = 0; S( y A0 w \" `2 U2 F
8 z) P) [+ A5 _/ n) G # 松弛操作 ! |" _0 p, {1 o/ s! u4 q d0 V$ S& n for _ in range(len(graph) - 1): , u6 w4 P* M3 {9 G1 |: f for u in graph:5 w, t/ O2 W9 H( }9 `
for v, weight in graph[u].items(): . r6 @/ R9 }( s y if distances[u] + weight < distances[v]:) I: o ?9 g) z, B5 n. t8 l$ F
distances[v] = distances[u] + weight 5 j, {( F0 D. D* Z2 r, {+ K 7 [) W7 R$ d; |; e2 U # 检查负权回路 0 F! O% a' C- J. |# D% n8 d for u in graph: 1 d# V; J& F1 n+ Q* o1 } for v, weight in graph[u].items(): " P/ R3 x1 s; _9 ^9 w2 H if distances[u] + weight < distances[v]: ; M# a) t9 O& q, d raise ValueError("Graph contains a negative weight cycle") ( R2 w0 Y$ R) u2 f2 n5 Z8 d! P8 F' O( |& \1 N
return distances \ m+ \7 t4 J, p: e* ~, d
9 \+ B$ c: l. ~8 q; H# 示例图(带有负权边)$ W8 x7 u/ j. ]; p* X2 L
graph_with_negative_weight = {7 I3 g# ?/ w& [4 T! _; J& Z
'A': {'B': 1, 'C': 4}, . |. e/ h; l5 y3 l, M& n0 [ 'B': {'C': -3, 'D': 2},! A0 i& H* k9 V, s6 C
'C': {}, 6 T! q8 w* Y, c* j( }, k 'D': {'A': -1}8 H5 Q( B- X; A, O
}- v4 v; H2 V4 o2 W# p1 L4 s7 R
+ [5 r& p- M8 Z& dstart_node = 'A' ; n3 R' o; I0 ushortest_paths = bellman_ford(graph_with_negative_weight, start_node)6 T. y- f% y0 @5 u
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}6 E, R# W5 a& v U; L
``` 3 Z/ B- d" R9 s! ^ 2 K8 c! L% w% X6 O## 3. Floyd-Warshall 算法 $ I# o0 z( E( N) F7 ]1 i' E" \6 n0 O4 L1 Y, t# S/ g* u
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。 % G9 G1 o5 c2 s2 {4 g' @ ) c8 M( `, B) Q+ N* o### 原理步骤 ) W: T6 [% X4 m/ `, e, t 7 p: N F! p' F0 i1. **初始化**:3 ]+ N+ Z+ n% Z3 L) a+ B9 R( p& V$ z
- 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。 x1 y! a; ?, B% [6 z6 }: g' l
: A# V& a5 R: v4 k' _" ]
2. **更新路径**:/ [3 [2 ]- u7 x, j4 l0 h+ T
- 三重循环遍历所有节点,以每个中间节点尝试更新路径。7 M6 Z8 c0 q+ N, U# y3 |
* w0 x! z; q. `+ d8 E9 G3. **输出结果**: . j' X( x# O& f, U - 最终得到的矩阵即为每对节点之间的最短路径长度。 5 W( y6 `8 l1 `0 A) y1 a, o7 D % e- i/ z" d( V" d1 e. X5 m# z### 示例代码 ) Y" |- f/ S! O* q& Z, a( f; d5 z( e3 y% ~
```python ( W" E8 z; n1 L6 ~0 g1 U* [def floyd_warshall(graph):: ?! Q1 M" j6 }7 s) N
# 初始化距离矩阵 % U& G, B3 W! N. ~* O& K5 }% I' | nodes = list(graph.keys()) $ |. G( n2 K" k, t! R X distances = {node: {n: float('inf') for n in nodes} for node in nodes}) F& h' n+ U" j N$ K6 L
& U3 z( O$ g" R2 D for u in nodes:+ x$ `/ b$ y( A, m) ?3 {4 L
distances[u][u] = 0 ) r8 n% a, @/ }) r for v, weight in graph[u].items(): . ^" [( T7 [; u6 O' { distances[u][v] = weight , k# o; W8 u$ o1 V6 W* ~6 R , A+ o1 x9 v: ] `; B # 更新路径 / @" x9 g6 ]7 i" C% B( N. \ for k in nodes: 3 ]2 R6 p; w% x# O6 A: u for i in nodes:* }, w2 a% f7 Y0 q# U
for j in nodes:: U2 A8 d4 O/ p# @4 b4 m& J
if distances[i][j] > distances[i][k] + distances[k][j]:/ T; ^& H- I1 q9 C% B# \
distances[i][j] = distances[i][k] + distances[k][j] 9 N# }5 v4 [5 W% |1 E, \& L5 ~$ p& i+ Q/ B( V- l' I9 k
return distances0 w! ~8 J8 p% \6 Z2 X& z