最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。 0 ^6 x! M0 s6 @/ j y$ T9 k0 f- } # e+ L# y* z" X' h% d## 1. Dijkstra 算法 8 H% b5 H- w/ k0 b# z- Y% J& N* z" q& }8 ], J1 x8 |) H. `$ Z
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。 . r- Y- Q M( G/ G" \3 S( |/ i4 R, {1 ]7 o
### 原理步骤0 ?) W1 Z+ j3 i+ o2 X5 D
, @$ s, Y6 V. X3 h. I, b
1. **初始化**: & o9 L# E1 g3 Q& T1 x, S - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。2 h+ v8 C' f3 p; b ?/ p6 O$ l
- 初始化一个空的优先队列,用于存储待处理的节点。) K9 @1 l, e: b c/ R U0 s
! _ z4 e' C# c# R! O
2. **处理节点**: c4 y' z0 k# v, C - 从优先队列中选取距离最小的节点作为当前节点。" j4 w$ S0 t5 ]7 d- p4 x: z7 x
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。 " V- M1 _, X K+ L; p. J9 B- {- m ) Y- J9 z; `9 p! I# Q) U, `3. **重复处理**: 0 P0 H1 L: k i - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。 ( T* ^& M0 `+ o! O5 E. [0 C 2 V4 H$ @' F# j# ?8 u0 F5 ]$ w0 c! Q### 示例代码 ) M. c6 a/ M/ ~+ S# D 1 W; B% @/ f' h/ E( K```python ?6 H' Z* B- { X
import heapq0 C/ f1 X; o1 Q( A- u/ U
5 T r1 ^9 U( G- N7 jdef dijkstra(graph, start): F- j l$ v# o' J3 |9 h # 图的表示为字典,键为节点,值为邻接节点及其边权3 N; L* @# Q7 c0 H. f
queue = [] , o3 P' [& f. } distances = {node: float('inf') for node in graph}* e B. u- r7 ` i3 g* ^$ v
distances[start] = 0 ' K( d5 C/ ~% a3 F* @ k9 u# _ heapq.heappush(queue, (0, start)) # (距离, 节点)' u) T, u4 m/ c. I) S
* q4 i; V9 c, V' o8 K
while queue: $ {$ e) m4 \9 l% T6 h6 F8 s, j! I current_distance, current_node = heapq.heappop(queue) 7 I% u0 S# q( E; x% Z! l. M) q* n% i # [' E3 n \& ]- u # 只处理当前节点的最短距离 . V) I7 P/ r& m% W" p9 o: i5 D if current_distance > distances[current_node]:3 g# }( s& i# P( q
continue ! ~4 x: C2 s- m% B* H 6 S/ I# _4 S" B4 s' Q for neighbor, weight in graph[current_node].items():( a( z. E, |, ?1 ^# e, D( ^# c- D: u
distance = current_distance + weight2 r+ U9 k% ]3 D9 v6 z/ H
. b* c. Y* e2 F: o+ W9 m8 I+ G/ [+ _3 T! f# B
# 更新邻接节点的距离5 j& Z; _3 a) z9 N# C6 A
if distance < distances[neighbor]:) M2 M- v* B7 F! p% {0 @
distances[neighbor] = distance i0 z: k" p1 D
heapq.heappush(queue, (distance, neighbor)) \0 o7 d5 d' R& w1 n! P2 V
; e/ f$ D5 e, v) |1 F9 h! h5 Y% @ return distances3 n3 K7 {+ i7 ]) h
0 @) E' A- J. S/ {1 V a
# 示例图 / ]) n C" |8 r' Cgraph = { + d# t! g$ N$ t+ ` 'A': {'B': 1, 'C': 4}, 8 K* q8 M2 U5 {% g 'B': {'A': 1, 'C': 2, 'D': 5},: ^) ]- O% K! [! b6 b* b4 k
'C': {'A': 4, 'B': 2, 'D': 1},; U/ ?7 T* K$ ^' K
'D': {'B': 5, 'C': 1},; n: g! {3 i2 U' K2 B6 T
}! |! i1 i9 i- q1 g
?8 x% d. z7 M1 v
start_node = 'A'0 ^1 h, J5 C. z% {# L6 x
shortest_paths = dijkstra(graph, start_node)( e/ }0 t& q6 U2 Q4 z7 D- G) r' ^
print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}" d( d0 O* r4 S) y0 M Q% t8 S
``` 9 t! R. S3 E) a0 U' _ 1 Y8 ~& y$ `7 O0 j# H## 2. Bellman-Ford 算法" o: _" U5 R) n6 _8 Y' O6 v X
! @, j: l3 G2 F
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。 5 q1 Q9 M# T5 Q3 S0 ^. X6 u" k f! g5 t# Z
### 原理步骤4 f5 p( }" B5 z
2 c. w4 R. e8 J1 n I
1. **初始化**:1 U) p" k6 {( B" F; i/ T
- 设置源节点到自己的距离为0,其他节点为无穷大。) m7 e; U4 }4 F4 L/ z
# R+ @* Y* K5 Q0 F) M2 } w
2. **松弛操作**: 3 A) q- W z1 b5 X - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。 9 {; G/ h3 D4 K: ~3 d( [) R; K+ ^ b
3. **检查负权回路**: # F- e5 X9 y6 l, W - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。 - k0 ~: z& [/ O$ n4 \- _ ' k, E* A5 C8 e9 y; C### 示例代码 3 W/ q/ B; g) u% r7 s' W$ E S 2 L! ^/ T( M3 Y8 L* U& P4 N' f```python8 i6 J% S/ G0 u9 Q3 t
def bellman_ford(graph, start): # ^9 ?7 ?" w$ g7 i8 C. e# F" Y # 初始化距离7 u# C% D$ y, \" B8 o/ R3 j
distances = {node: float('inf') for node in graph} ; ~/ X' G, ]8 O/ \' y3 H distances[start] = 0# a6 y& |) W, g3 L* S
1 d+ q6 @. z; S6 U' }% ~, ?8 `" `3 }5 p3 v6 S # 松弛操作 1 B% q9 z0 q) k( Y/ K. c! ] for _ in range(len(graph) - 1):4 `1 q! @4 P' X" n B
for u in graph: * n& d! |6 W: K' `% R for v, weight in graph[u].items():( T* I* r1 T9 U6 Z
if distances[u] + weight < distances[v]:1 K2 _3 K% o7 F4 K% _1 a- ~
distances[v] = distances[u] + weight: x E$ c W; H k5 N: s
8 r$ {# O- f3 ?$ O # 检查负权回路# F5 M# r0 a) ^. X% A1 V" Y
for u in graph:# A ~$ {2 K( P2 a
for v, weight in graph[u].items():; n) J: c) @8 H2 I. K4 C/ |
if distances[u] + weight < distances[v]:; x) F; l8 F. X: ?' e3 O
raise ValueError("Graph contains a negative weight cycle") : O( p) m4 ^/ I 8 `. M; p' t" e. Z/ `6 D+ D- F9 b+ N return distances 5 C( `. ?% H, v) M3 X( j, s 9 E( B$ r* t+ t; B) H, ?3 \! i# 示例图(带有负权边)* ?! G c/ _4 s( |# _& K# [
graph_with_negative_weight = { L' Q7 O9 X+ C. Y r
'A': {'B': 1, 'C': 4},8 O: u9 S) g& s2 I+ X/ j+ u* K
'B': {'C': -3, 'D': 2}, m& j, W5 k# u6 B% k( C
'C': {}, r9 s0 I' {- a' y* a/ n K 'D': {'A': -1} ' f( \2 ], K; D, K/ a} , K- H4 F1 X7 H' g , d1 N1 K7 a5 Gstart_node = 'A'/ ?% `9 f. S9 L, [ w
shortest_paths = bellman_ford(graph_with_negative_weight, start_node): K5 M3 H! ^- V
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3} % y# |8 p4 L4 @- O6 a5 u5 _" c``` * P) A" t7 ^3 b% ]5 w $ S4 r' y+ r8 V m## 3. Floyd-Warshall 算法, _- O. N) z# W; n3 \
' V4 n% N0 [' p2 e) GFloyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。 + T3 {' \# D* f+ {6 V! T; ~3 w3 j) X& {2 H( G \6 H0 a8 c( N
### 原理步骤 # ]! _' O# w( U% U) l; R8 b ! b1 _9 F* x s# a% L9 N1. **初始化**: . N' f+ C5 c" {4 T9 |+ L w2 C - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。, X" s+ O& j, R6 R
% }9 u; e8 x; B. H/ d4 a2. **更新路径**: 8 ?* d; ]* `, @ - 三重循环遍历所有节点,以每个中间节点尝试更新路径。 2 J# Q# E* S, Z: q' f) [* k- U 9 L, F% I$ D2 G. }3. **输出结果**: 7 q" |& o- O( }4 e) l - 最终得到的矩阵即为每对节点之间的最短路径长度。 & b/ J/ m2 z" u- b$ |9 D9 Q. N P
### 示例代码) @; C; ^# O. b9 T: h' U
! o- U3 R6 ~/ K$ o```python. A! {4 B$ S; v, }7 R4 D: [
def floyd_warshall(graph):9 ?& a1 b- [ y1 u) q% ]
# 初始化距离矩阵 - b% y. g1 @0 e8 l6 V \ nodes = list(graph.keys())7 C/ `) r0 \3 N9 ]! ?, P' F
distances = {node: {n: float('inf') for n in nodes} for node in nodes} 1 L7 ~ S. x4 |. ]+ U" q/ K" s% s, @% g7 t
for u in nodes: , U7 J: s/ A2 s% i distances[u][u] = 04 d( I M2 U6 N( J
for v, weight in graph[u].items(): & A& f! f+ z L. ^7 N8 r distances[u][v] = weight . t; b F% M; t/ ]. }9 y9 \" h) J& R+ l2 e8 h4 p
# 更新路径$ r a# X' o) c) D$ O5 @! ^
for k in nodes:2 G( A G" |/ |1 H0 ` X9 C
for i in nodes: % D. h- R' y/ W# ~4 h for j in nodes: 7 U4 z2 l% |+ [, f if distances[i][j] > distances[i][k] + distances[k][j]: . H7 y9 f; k3 c" ^" N- Z; F distances[i][j] = distances[i][k] + distances[k][j], [) ^- V/ L+ E$ b' v& d/ d- A
; n; t' A& i& u c l( s; M& W; X
return distances1 f1 i N* H2 K7 {: A: q1 h8 r