最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。 2 Q" `$ B1 |- p. K# x. U' W $ h5 A* V& C) b$ l0 x1 B* ^* E1 P## 1. Dijkstra 算法 1 W) G% v1 I) _1 x5 z5 G7 ~# D( _. s" T7 U0 }0 A
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。 + B- e! j- L' p+ ^0 R! s% T3 X# f* ?9 ]6 D9 d( y
### 原理步骤 / g6 c6 x6 ~. D& v" J7 G2 i( @* A- I2 T3 ~7 x8 c: d
1. **初始化**: 4 |+ m. _" {* n8 X% d. C - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。; g1 j. w- Q8 F" p* X L5 i1 ^% L/ {
- 初始化一个空的优先队列,用于存储待处理的节点。: K5 ]% j- s: M; d/ R# N
; Y6 j9 F8 a! y) c( V; Z
2. **处理节点**:- ^$ ^* v6 p! j9 t5 h
- 从优先队列中选取距离最小的节点作为当前节点。 6 x7 ~! ]! S/ D& L4 w8 l/ n - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。$ W+ h" {9 P4 K( k7 U
: G& k% Q" b ?2 Y2 ]5 c
3. **重复处理**:+ S$ \; E( B( C" j1 P( _
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。 . q0 Q1 z9 V) j3 w/ k/ `2 q ! u7 U( ~" i) K+ w! H, t& v### 示例代码- ?) G; K7 O/ z& Q5 j7 v
( h1 o3 R, @+ S- b3 [8 O
```python * T- Q* P2 n# ~" i# h x$ p$ cimport heapq : b$ ?# Z) G# p% R: W1 m$ N8 M% h6 r8 ?
def dijkstra(graph, start): . v; o- L4 {( k: L: A [8 B # 图的表示为字典,键为节点,值为邻接节点及其边权 ) Y9 R4 L0 A7 m! Z; g3 ^3 B/ P queue = [] % v& ?& L5 V7 ^5 p) E3 p4 A' a4 n- V distances = {node: float('inf') for node in graph}- F. }2 @2 S0 D: W: a9 h
distances[start] = 0$ i* o, F* u6 Y# ?5 s! m8 T. G
heapq.heappush(queue, (0, start)) # (距离, 节点) 3 i- Z$ x) X7 h q3 F' V& q0 U5 z8 V2 _1 J! S& {
while queue: ; {5 J' M3 ?+ V2 M. r# G( q8 B$ _ current_distance, current_node = heapq.heappop(queue)& D2 u* V& \: \6 j, |' W
5 ]7 L4 u: P4 m' v/ [4 b& y1 }
# 只处理当前节点的最短距离 1 U; M' n* A, Q/ a; U! s* S if current_distance > distances[current_node]:6 t% {; b3 v% Q d. U( ^, ^ G
continue " _, T! p$ u$ ~4 M. t: Y& M / |; a9 I9 }7 n$ b for neighbor, weight in graph[current_node].items(): 9 G0 e5 A; z. ]( d9 [+ {) B distance = current_distance + weight/ K, ?8 H j1 O' }# u
6 S3 g% S8 w5 L h. x- d9 Z" o # 更新邻接节点的距离 , k' e" i0 k7 f# R* h: ~5 P if distance < distances[neighbor]: 9 }6 l' z4 N- N3 L5 _( u distances[neighbor] = distance . P* b6 v' M. L. `7 `- y2 a heapq.heappush(queue, (distance, neighbor))7 O& |5 e* q: ?. O* Z9 x
6 I4 t$ H& p8 W' q9 |9 Z return distances- V% Z- | p2 z2 f- e! R
; ~. y% O U, x! j! I
# 示例图7 U3 ^/ P3 h: O
graph = { $ d+ _& ]( q0 A; m2 O 'A': {'B': 1, 'C': 4}, 8 E( z. V H: m$ x 'B': {'A': 1, 'C': 2, 'D': 5},) R. q: \' M' |
'C': {'A': 4, 'B': 2, 'D': 1}, / s# n. ?/ \' b/ l) ]* @ 'D': {'B': 5, 'C': 1},0 {9 c: ~3 E4 g8 ]2 l
} . R+ g% Y! w2 X0 u9 l/ T 6 W. c _: X$ l9 t) \2 bstart_node = 'A' 4 P$ f" B) C3 C* p* \1 V6 f+ Fshortest_paths = dijkstra(graph, start_node) 7 l q: K/ p, G1 pprint(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4} _) A( u! J$ q, y% Y0 Z, u``` + Z }' H P$ H2 Y) `# c ' I8 R! l Z' F/ S4 p; u0 A: U! X## 2. Bellman-Ford 算法 + R6 ~. B1 f/ O1 H 7 x; h& D- F0 |$ m8 j2 b7 pBellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。 ) a- {' x4 ?1 y: M. q" ~4 u5 o- a1 t
### 原理步骤 & N6 G- f2 C$ l ; {0 X$ x' ]3 I2 q+ a- R- J. h1. **初始化**: / H$ \) ?+ r7 p2 F+ N( w# E$ Y - 设置源节点到自己的距离为0,其他节点为无穷大。! }6 `: F* F& q
' X, j" A% Z( L2. **松弛操作**: 6 s" X; N+ m5 P1 O( j. K - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。1 j5 h" Q4 g- | W
7 P& a5 t# I% e! H& v
3. **检查负权回路**:- O( i4 `5 l( ^
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。6 W2 g* x* T( ^: [) B
; o% E Y9 R$ o9 j9 p, v
### 示例代码* X7 Z- {; C; {, V3 {! w3 D2 s3 ?
4 i) X& X P( r. t# W8 H! L```python$ m! x1 A* A, S, D* M
def bellman_ford(graph, start): ! x) |8 h, z! J4 } # 初始化距离 ( n( T' g. B4 c$ {8 u distances = {node: float('inf') for node in graph}8 W. f$ @. r+ h" e$ e
distances[start] = 03 ]& b; \4 P- G0 \* x% d& u2 I
" D% U' J4 H; F% B
# 松弛操作3 t; u1 H- m- f& i% g5 R$ @
for _ in range(len(graph) - 1): 0 n1 z' v4 V: ?6 X" ^+ D B for u in graph: # ]* h; e% a6 X2 x for v, weight in graph[u].items():$ p* a5 N5 b' e! {; L! n, C
if distances[u] + weight < distances[v]:" E) c! A2 e' ?+ H, o
distances[v] = distances[u] + weight' u5 r4 g& Q1 r1 o+ U3 ~, d3 g
- S$ w# b/ U* y' A
# 检查负权回路 f2 a# f+ ~- F& J& o3 r, R' y for u in graph:/ e5 \/ e2 J+ p- `0 G
for v, weight in graph[u].items():; g% `# V" A9 A' S
if distances[u] + weight < distances[v]:+ g! y+ A! v- H3 H' o7 F7 G
raise ValueError("Graph contains a negative weight cycle")4 W# ]8 ~ E; ~& X
8 m5 _/ M" X0 ?: Q; m+ _
return distances) E! J2 X: s/ }( V& G
# g& m0 `- I+ e( U D6 W# 示例图(带有负权边)- k$ g. q7 I8 ` D$ g. C
graph_with_negative_weight = {7 Y8 m0 ^' w" t
'A': {'B': 1, 'C': 4}, $ Z2 ?8 [: x" t 'B': {'C': -3, 'D': 2},5 y% g/ n3 F" ~, I w7 {- S# g
'C': {},7 G5 ]' \, z3 Q5 [0 R4 b
'D': {'A': -1}3 B' F3 q9 ~/ R2 U% o5 f# E
}5 \5 q7 d. v* ]