- 在线时间
- 479 小时
- 最后登录
- 2026-4-17
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7790 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2923
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1171
- 主题
- 1186
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。
, k% b, y# l1 s8 L
8 R: [; V, S1 ]9 j: O## 1. Dijkstra 算法& ]' i; j% D2 D8 ?( R* @1 W8 E" x+ ?
) [0 X4 I/ |8 ?
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
4 m4 s0 O) W+ }9 Y$ F
% f7 b6 C: }: e### 原理步骤6 {8 I9 B! E6 k; ?; @
+ I6 ~5 h2 d! k2 S) ]1. **初始化**:3 h" x4 ^( ]% M& G
- 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。
i0 V& p6 U1 W* l7 e9 U( D - 初始化一个空的优先队列,用于存储待处理的节点。
5 D5 g. }. l' d# b9 [1 @+ W0 Y
/ H0 G0 z8 u8 q4 ?9 F2. **处理节点**:
! D% V6 T% B+ q. I" c# Y - 从优先队列中选取距离最小的节点作为当前节点。
$ b7 Q g# h) }8 | - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。; |" X. H8 _1 b% U1 D
& j. U% M' z0 p0 |, `; B" T; m
3. **重复处理**:
2 S( A' w2 [2 m s! D - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。1 ~' r! J2 Z* ^! a( g
7 e! T1 T) m. n
### 示例代码
9 |7 ~6 I6 u& i! e" q9 J3 s9 ]$ t: a W( t
```python8 o! }1 P$ O5 Y8 A
import heapq
# X, B% j; C5 R/ }5 e8 ]- U8 M# }$ V! }3 a( r5 P
def dijkstra(graph, start):/ ?* j( n2 V5 @; k' ~) l
# 图的表示为字典,键为节点,值为邻接节点及其边权
3 N, W/ m H: K& m% H% m queue = []
' c1 i7 L1 ^% `* Y0 d distances = {node: float('inf') for node in graph}
) f- _1 \. a0 K/ K, E3 p) k, Y) O distances[start] = 0 ~# f7 k& S: U0 C1 i$ @
heapq.heappush(queue, (0, start)) # (距离, 节点): T8 @8 U/ ?! @. @1 p
6 N; \2 O+ w( Z+ Y+ ^ while queue:
4 b* L9 l7 }5 e6 ^" U4 k current_distance, current_node = heapq.heappop(queue)$ m* d) X! @' v V0 M. s
. y& O- p [9 Y) R! b # 只处理当前节点的最短距离2 ]5 Y" H8 k' a/ k1 F, N! C1 E9 ?! R
if current_distance > distances[current_node]:
; p6 b5 ^ }+ r7 f; c2 i continue7 [7 k1 z+ k, U' B: U% {
, h! i0 z9 j, ~& x. g* x2 W
for neighbor, weight in graph[current_node].items():0 s# V3 _6 y% D, ~" V
distance = current_distance + weight
2 }& N& I: l/ m' Y. ]$ x- l8 K
' e1 C& I \) D/ J; A& | # 更新邻接节点的距离0 [! R/ ~% Z1 S: i- d
if distance < distances[neighbor]:, \1 i2 }+ D# q4 B5 a
distances[neighbor] = distance, H0 {' S0 _. h5 {6 {
heapq.heappush(queue, (distance, neighbor))
2 M+ f, R# J. z% O* G, i+ \# M5 d* E* {
return distances8 M7 h9 O! _# {7 c. P2 g
) f6 G- I" w' D j% Q2 U& X$ q
# 示例图
) e$ ]) a4 r- ]3 O) }' F1 bgraph = {
1 D3 Z8 z0 F' f. B, s( N 'A': {'B': 1, 'C': 4},
! d* o0 j/ P8 j9 V( N! E% X3 z 'B': {'A': 1, 'C': 2, 'D': 5},0 R5 ?& u" r4 v/ N% `/ U' D
'C': {'A': 4, 'B': 2, 'D': 1},
) n: o- |5 G/ g: Q 'D': {'B': 5, 'C': 1},' f) ?4 t+ U7 K, u J0 A* o9 s
}; p$ B U# @2 @! z0 z
: _$ L3 ` }! k5 ]2 o
start_node = 'A'
* y* z4 h. z* ?6 {$ Gshortest_paths = dijkstra(graph, start_node)
3 E$ Z* G8 J2 d3 O7 ^print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}& u* O c8 b. F
```
; `( {3 L# s& I2 o0 N
, O( d, ?" W, J" r6 c1 f## 2. Bellman-Ford 算法
. c! q2 o8 C% b# j4 ]8 @. D2 B
- ]. @$ z) W* a8 z% PBellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。: [* w2 W; j4 i5 F/ c
. m: w4 }) ?( s5 H7 d/ \
### 原理步骤( e1 o3 q3 n! z, D
2 H! M# B$ h& ^7 p. d4 Y. p
1. **初始化**:
" w% o# Z, i8 ^! X5 g, [. G - 设置源节点到自己的距离为0,其他节点为无穷大。
8 v& i' f, w8 Y0 d+ }6 D3 ^9 z4 y. a" t9 }8 ?. s$ k
2. **松弛操作**:
L& m2 F; x1 \5 a0 }5 s - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。
( R7 F5 L& h2 v. q' m, q: ^* V8 |+ f
3. **检查负权回路**:) I0 e! V. v: d7 p. }4 w
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。
6 h4 D6 s; w& T% Z3 t& z, p
1 [0 k- h+ t% [; U### 示例代码. U$ V8 s# I% F/ I' ^* Z* z1 ~
7 w& M/ J0 i7 |$ q0 u```python
! s5 x; ~$ e3 Gdef bellman_ford(graph, start):9 k2 z z S- D& u+ |7 k% e& y3 M
# 初始化距离7 j X4 H! J; Y1 D" C* [: X
distances = {node: float('inf') for node in graph}
/ o; \4 e6 Q7 k% o+ W$ p2 N+ h distances[start] = 0
8 K0 M8 [5 [& B i. a- K
' s6 U/ O; h; h7 L7 \# R # 松弛操作
8 u$ K1 Z6 M8 `6 l N% ] for _ in range(len(graph) - 1):% O# Y4 R. _! y$ J
for u in graph:
& ^: Q* n$ q9 r9 X! ~ for v, weight in graph[u].items():: a/ W2 I* ^6 F2 Z. f( Q
if distances[u] + weight < distances[v]:, X, c& d7 r3 s0 f$ [
distances[v] = distances[u] + weight8 S( C8 }0 y) `7 j& u% D
; s7 l, P( ^/ G0 h$ o, I9 Z # 检查负权回路
: h4 n8 m# E2 |/ r; a( y for u in graph:
) s" X* {5 z/ [/ u' E for v, weight in graph[u].items():. O# Z3 E# X8 a% \8 z+ {( T
if distances[u] + weight < distances[v]:# a" E3 o0 ]) {) K1 c
raise ValueError("Graph contains a negative weight cycle"), Z) v9 h, q/ L) T! h
2 L) |& u; n" a1 X' c( \7 T! K- z
return distances8 c+ A" L0 E% u. j
+ X) u8 s$ J2 K$ ]9 I# 示例图(带有负权边)
8 n9 u6 b" [1 O7 s8 I* X8 Igraph_with_negative_weight = {
4 G; M$ q9 J" p c3 I6 a2 ^+ w 'A': {'B': 1, 'C': 4},' L1 d; i Y4 g$ K* L& o9 j
'B': {'C': -3, 'D': 2},6 H* X; _/ P% z
'C': {},
% P: s) @6 y$ G- a/ Y 'D': {'A': -1}$ r6 `9 r/ W: N6 |" D' t" m
}( b$ x f5 Z" J$ Q. ]
0 I a. q( V$ A9 d7 p. ^+ ]start_node = 'A'# P. l- Y9 M2 l. b O: ?+ i
shortest_paths = bellman_ford(graph_with_negative_weight, start_node)
* H0 n2 z4 i+ e- H3 ?6 |8 k0 }print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}
- n7 ^; P; G( f% a```
- C7 ]3 t( o( A$ @" W6 e3 i
3 \; E9 ]! u% d0 S& D) \7 h \## 3. Floyd-Warshall 算法4 q8 Q# t! s* J5 b' ?7 d+ D
7 M' ~) `4 \( G. _- ]Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
8 {6 W$ i6 p! U9 A# l7 T. p
$ ]' g* }8 M |' s5 @3 K### 原理步骤& F b, t9 A, F6 c1 P
* ] n% t4 E5 P1 ^/ R1. **初始化**:0 E$ f/ v# P9 l
- 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。* \7 U" l' M& {! D5 J7 o: C- x4 m
) a9 F$ J7 l/ n. ~3 r2 \2. **更新路径**:
4 F7 u8 G8 w* j- d5 a( ]3 a* W. y - 三重循环遍历所有节点,以每个中间节点尝试更新路径。
* z N0 o* P! E2 A: A6 f) b! q
3 i& A( z; u" j; H* A: f3. **输出结果**:
; H7 g8 a' V3 l( G4 d& t5 }/ ^% z - 最终得到的矩阵即为每对节点之间的最短路径长度。
( v2 T- y& R* L) O, X0 P3 @7 t0 ~
+ `8 e7 q/ x& S& z+ X; G+ ]### 示例代码 `# P/ a5 q: g! ^
0 a% q' Z7 L- @8 Z% Q& Z```python9 @/ h! t* Z* P: D
def floyd_warshall(graph):! ?* _/ H% n4 x5 @
# 初始化距离矩阵
5 l7 k) G6 @' U9 W( x nodes = list(graph.keys()) h7 `: o- s2 f5 e
distances = {node: {n: float('inf') for n in nodes} for node in nodes} X- |; ?0 C" M
7 m, K3 ~5 j9 B3 x4 }3 T# S) U% l for u in nodes:
" s. C* e! z/ }# L( I1 }( ~ distances[u][u] = 06 c0 `/ P3 l7 P9 M: z4 F
for v, weight in graph[u].items():
5 s# w6 [/ ?0 D3 O/ y# Y# D distances[u][v] = weight
6 U! ~0 T5 z3 F( m5 J
4 V: @! w! f3 I. Z # 更新路径
! M+ E& b( P# N" B W for k in nodes:
7 ]& m) R3 p. {5 \( p' ] for i in nodes:
2 W; \1 H" p j9 a7 J for j in nodes:
$ O& [! n+ A3 j; E: M) W if distances[i][j] > distances[i][k] + distances[k][j]:
6 g& r+ G5 W; J6 Q. I distances[i][j] = distances[i][k] + distances[k][j]
, @$ A3 y' V! [- a
$ b0 q5 A% k& ~' }4 W return distances
1 h; p% V2 n3 g; K# I3 K1 r+ ^
. d, C8 A3 C6 t" t9 j# 示例图(可以含负权边)- @( o6 o) h( ^7 z0 p+ |# ~
graph_for_floyd = {
3 L! p6 Q8 Z/ f0 P# p$ |$ o 'A': {'B': 3, 'C': 8, 'D': -4},; f, M( M \8 G5 z# q3 H) q- C) E7 C
'B': {'C': 1, 'D': 7},
+ }5 r6 O+ W/ D0 ? 'C': {'B': 4},
/ k. j( f1 S) F+ @4 Z$ l+ n5 G 'D': {'A': 2, 'C': -5}8 e' P% ?% `% _) c
}3 w, ^, N& v8 M) T% M& u2 j$ v: ? m
- S5 u( b: o1 k+ q9 u1 V1 e0 nshortest_paths_matrix = floyd_warshall(graph_for_floyd)
' ~3 [& V1 W K9 Rfor row in shortest_paths_matrix.items():
5 U7 W) c( b% T+ {$ g print(row)0 Q! k$ }" w6 s6 g" b% r
```
# M# |* W( v; V
2 \. p) }' k1 o* ?, b( v p0 @## 总结5 d6 X8 p* ^. V2 b5 y
5 P3 d! W. ^( H2 O- j0 S2 `9 U- d* V4 x
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
1 c5 K9 b+ P7 e" ]3 j$ ~/ j1 D- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。( x9 h" D8 d% B) }& k X
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。; b. [1 Y* M' K6 r9 r
1 d6 J, x0 O9 t* ^8 ?9 X
不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!# t& }7 V; C) J8 |$ J) B
4 A: p7 E4 A3 o
; O$ n- k* l! J8 P: {4 @9 o8 e# \4 t9 A/ i c" q0 n3 V$ d
|
zan
|