- 在线时间
- 480 小时
- 最后登录
- 2026-6-1
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7823 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2934
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1174
- 主题
- 1189
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。
- o% w2 U8 Y* `" Q
* ]8 y0 R2 K0 c( b( @## 1. Dijkstra 算法' j* Q+ k% ?$ ^' l- g
) }2 C9 X" w( O* B2 ]- Q" rDijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。& X5 M* z9 @$ {. q
# y* P4 [0 b0 w, F$ }### 原理步骤
6 C* S5 T' f3 U6 V0 X- K
2 D; g8 u$ r+ o# o$ [1. **初始化**:
2 D9 D$ k1 X. H( m6 \$ N: W% w8 a- f - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。. B& N3 C+ d* x4 M: L
- 初始化一个空的优先队列,用于存储待处理的节点。
; v z1 A4 ?9 t8 }
5 b: ^+ E3 w$ M, w# F2. **处理节点**:
# }2 N. D( y7 T0 x' S% u - 从优先队列中选取距离最小的节点作为当前节点。
* o1 j% u6 Q8 J - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
0 A5 n& D, g8 W* j+ T7 H
4 m9 O3 ~2 |9 M* r+ n% t3. **重复处理**:3 w4 J# T5 q- v0 j8 _4 P
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。
; V v# B3 f, @0 l/ r. c5 l* F j7 D: G W
### 示例代码
8 r, w$ V; e0 r. Q1 C( d/ J$ i* y. Z/ U7 [/ E
```python
7 F( `) X5 d" T* ~' _# timport heapq4 H2 H1 q( P9 b/ |4 Q
& R M5 V6 N2 g% t C" idef dijkstra(graph, start):
/ Z# m+ n6 N3 ~# [1 a # 图的表示为字典,键为节点,值为邻接节点及其边权: P# m0 e; R& H# v2 a
queue = []& Y2 j- s* M |7 Y( n, y) b0 g
distances = {node: float('inf') for node in graph}& {4 `0 K8 g8 S$ v- A0 J6 k
distances[start] = 0
, C/ p- E# K, d6 g heapq.heappush(queue, (0, start)) # (距离, 节点)
/ O4 @2 u( B' W) W$ R7 e* p
' s; r- x3 S- H4 V5 I" F) R while queue:3 t- o# \. ]3 ~8 N( ?6 N
current_distance, current_node = heapq.heappop(queue)
- y# g. F& {8 V/ X/ y5 t; f7 D9 ^& p6 T1 }& _# j* N
# 只处理当前节点的最短距离
" Q# a, q, Y5 \5 T0 ^ if current_distance > distances[current_node]:
6 _2 D% J3 |6 k p continue7 y$ n* q! u. ^) D, j5 T* C
/ P8 e3 k9 H |* ~) A
for neighbor, weight in graph[current_node].items():
& d3 k9 s# F( Z' @ distance = current_distance + weight
" ~3 f9 ^9 ?# |) {4 L) |
+ G6 T$ f+ s2 u0 M2 M, M # 更新邻接节点的距离" H2 L4 d; Z: \: ^
if distance < distances[neighbor]:
$ {; N+ w" e8 F' \. V distances[neighbor] = distance
6 E) E. I, P, ^& j; W heapq.heappush(queue, (distance, neighbor))
! M$ y! r" f5 p$ c% n U6 L% t# P9 p A
return distances, c( ]6 H# f" P" Q7 c' ^
3 `! f( [. x2 N0 @# 示例图
5 P/ Z0 e r; jgraph = {
D0 Q( w" X1 x4 ]/ g+ j! T1 _ 'A': {'B': 1, 'C': 4},
, t _+ n' b4 w3 Z& V" I* }! Q 'B': {'A': 1, 'C': 2, 'D': 5},
/ N9 C; k5 u: |4 b6 a7 i 'C': {'A': 4, 'B': 2, 'D': 1},' T! v" s3 L6 [! | u7 _
'D': {'B': 5, 'C': 1},; e; x$ f) r0 X. P: S+ w
}
; _1 |: _" b8 |8 [* b, e. P2 k& N3 ~ R$ ^
start_node = 'A', F, D0 U- y* U
shortest_paths = dijkstra(graph, start_node)
$ _$ b8 l$ _' d' h# h0 Zprint(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
) c8 N/ G6 }# o) A( P$ A```
; S) ?/ \# x- C) t- `& M7 v, d4 n* U. X! P3 G' q3 }9 ^
## 2. Bellman-Ford 算法
# W8 c% [6 f7 ^* F$ M; K
$ M- \9 { Q7 h4 N8 t; G. m7 yBellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。- i- S a* l% ~! n4 |2 ~
" w4 p8 d" \9 ^4 H
### 原理步骤
/ D# d- Z. l8 A& m7 A% o: b2 b+ M. P' C9 N. x: v5 K
1. **初始化**:
+ s5 E8 V; b( \/ p& ?* M - 设置源节点到自己的距离为0,其他节点为无穷大。
, d$ O: i' L- `7 e/ G* X8 n; G+ r! W$ u" o' A5 B
2. **松弛操作**:" v% P1 t2 v3 Z/ g) T* r
- 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。( E, x4 A) ]: ^
. G9 s* U% ~: B5 x3. **检查负权回路**:, z6 @ m, f4 D2 E- q+ p+ {6 F
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。0 T- P$ d# I* t+ g9 V) d Q
# O/ e+ d' T) ^( w! b# O- Q### 示例代码$ ]* w" n5 W& O7 R) j9 ?
5 h7 t( k) j: h# E```python
1 ~! M. K9 L2 ?4 Fdef bellman_ford(graph, start):
* X; f) ?) l1 ]/ R # 初始化距离4 G( S( n" q$ N; a' r+ X
distances = {node: float('inf') for node in graph}0 U, X7 D9 q& C/ v+ e I
distances[start] = 0
0 p4 v E' k& Z2 [9 g5 H9 v! J0 A$ j3 b) h2 c! v" T$ Q+ r5 Y
# 松弛操作: [- ~1 J$ ?1 t8 B
for _ in range(len(graph) - 1):
" P" n' i3 L5 O9 h$ e s! m for u in graph:' J. O$ ]& F: l3 q
for v, weight in graph[u].items():
( L$ ?& U# m7 w9 ~+ y if distances[u] + weight < distances[v]:8 S3 G7 T) U2 _- A. S
distances[v] = distances[u] + weight8 ]/ d9 u9 y9 W$ M
/ T9 w4 ?0 B/ [* j7 C
# 检查负权回路3 m2 A: W9 \& z9 ^' s
for u in graph:5 \; ?( c7 {4 B
for v, weight in graph[u].items():( f8 c9 z- f4 H; n; {
if distances[u] + weight < distances[v]:: [2 u& N# O* U; j
raise ValueError("Graph contains a negative weight cycle")
# ]# @1 r3 i; ?9 J9 b. n8 C, D; q0 V9 J
return distances- R& C3 t; e" k
; s( B/ _' k3 r. T P
# 示例图(带有负权边)
5 r% U( h# H$ M' |graph_with_negative_weight = {
# O8 {. j( e( m& J. l: a1 N2 t 'A': {'B': 1, 'C': 4},/ o* {+ e8 B( {8 y1 v, G! S
'B': {'C': -3, 'D': 2},4 }9 T5 H2 ~( d) O3 R
'C': {},
8 q9 C' Y& h- }$ f 'D': {'A': -1}
7 Y. Y c* y* ]}
1 ]- V6 Q' W$ k+ O
8 ~2 u& R; i& ^! Gstart_node = 'A'9 C7 n; l- @- U: n: `/ J3 i, w& l
shortest_paths = bellman_ford(graph_with_negative_weight, start_node)
% M3 E& h( G5 R* |1 dprint(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}
+ W9 Q! v n2 h, J7 u+ y2 x' m```
" C9 t2 o+ p4 m2 c8 L( k6 y7 K2 A. e7 [8 w
## 3. Floyd-Warshall 算法8 m) z( ]% I3 x: ]$ |( w
7 h6 O( T+ g7 D L( V) ?
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
3 h! ?8 g9 ]' R4 P5 \. i# K+ R4 I1 m
### 原理步骤
' O8 [3 Z8 z3 C/ z& {( t
) H. L9 ? b* k3 D; `+ f9 ~. p. b1. **初始化**:1 f# z4 Q" J) G3 h
- 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。
0 [$ b; o0 C4 X4 M
, u9 I( A9 R& ]. E0 }8 K: L2. **更新路径**:' ?6 R) q6 X6 H* W
- 三重循环遍历所有节点,以每个中间节点尝试更新路径。& j3 O1 q9 B* _0 [1 }9 \
; x+ d/ T- u- Y* i) D! g& i3. **输出结果**:
6 j% i1 e+ }) t) Y" p% f6 S - 最终得到的矩阵即为每对节点之间的最短路径长度。
& x* }5 l) w" z' H, ?0 i6 b# u7 L! [- e3 v: y
### 示例代码$ y4 T+ M, U3 \
: H+ Q; `& Y( @" g```python' n9 S+ ~+ d' W+ k! H: ^3 M5 E
def floyd_warshall(graph):+ I' b3 i0 Z& q/ N
# 初始化距离矩阵
5 `. p2 o4 J9 E( L) \+ [- c nodes = list(graph.keys())3 W! t6 Q* Q- U0 f
distances = {node: {n: float('inf') for n in nodes} for node in nodes}
# K0 Q( U; J) s: U$ J9 Q1 g9 u- d/ P1 o3 X
for u in nodes:$ R' ?) Z9 b- r: R6 H7 A
distances[u][u] = 02 C, n$ v/ M8 S. J/ ^) g4 g& V
for v, weight in graph[u].items():
, g( } K1 Q0 Q distances[u][v] = weight" r" p% y4 g! K
* H' l. `, s" H5 O. D2 u+ S # 更新路径7 b+ E) Y$ g# z8 I- Y
for k in nodes:
" V [* m# v5 u. L: o; h for i in nodes:
0 V2 E5 E, ^$ C" M# q# r! L9 ~0 Z for j in nodes:2 J, Z1 b$ ~) f( }* v9 |
if distances[i][j] > distances[i][k] + distances[k][j]:
0 o, U* `6 [: N& n" G' G) s distances[i][j] = distances[i][k] + distances[k][j]
! l/ W8 h; c4 q" `7 h; l1 J `( }7 D
# l- Y! ^( P* C- ?4 g return distances
# P& {. k" v9 G2 L6 p, l0 a7 q
& {! w% }6 {+ c2 \# 示例图(可以含负权边)
8 J2 j4 b: Y1 H2 D7 Cgraph_for_floyd = {, |$ v9 h: _( G& K2 v: q
'A': {'B': 3, 'C': 8, 'D': -4},
5 G `* Z1 I8 M, } 'B': {'C': 1, 'D': 7},- l+ l( Y) K. [1 m
'C': {'B': 4},
5 N5 d6 c* ~; p 'D': {'A': 2, 'C': -5}
# |. b* R, G4 v$ r. B}' W9 |, b8 m! K% L- [6 `! ?: ~
. R( {8 d- r- K7 A' z
shortest_paths_matrix = floyd_warshall(graph_for_floyd): p+ t1 F" l8 R6 i \4 @- e3 M
for row in shortest_paths_matrix.items():
) w) w0 }* L* H* ^ print(row)9 K! j; T) \8 U* v
```2 T. {# v7 `, ^) y, {( h* L- V6 E
8 w: \, G, u _. m4 J) k: j/ F; M
## 总结" v2 e& ^8 a7 ]/ C3 w1 K7 T
0 S. d( e. C. F+ W! f2 ?1 D) c- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
7 a/ H4 X! |3 ?' u9 h- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。( q+ \" X# e! W
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。/ {4 x b: d1 g" U! e8 _ N" x
7 y# B8 i% a, f+ u, H: h5 q+ j不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!# K/ ~) C) z$ ]/ s0 T
" W4 t9 W2 Q% C* \! i2 I
. b; S# _/ Y" h( c
. R0 n4 i' O: l) ?6 D0 ~ |
zan
|