在线时间 479 小时 最后登录 2026-4-17 注册时间 2023-7-11 听众数 4 收听数 0 能力 0 分 体力 7792 点 威望 0 点 阅读权限 255 积分 2923 相册 0 日志 0 记录 0 帖子 1171 主题 1186 精华 0 分享 0 好友 1
该用户从未签到
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。. r9 u9 C& o' i" H1 n, Q. P3 T' x
8 Q5 c3 T5 x: f. A2 D; y i
## 1. Dijkstra 算法1 L! `/ ]$ p* m7 u2 I) W ]
, P' X H. S9 [9 d' M) g
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。2 w9 Z: M/ i7 y7 F4 z) ?
+ N1 R% [6 i$ O+ @/ g4 y- o ### 原理步骤. @/ l8 y( T* I% U
2 r6 u( Z! P3 X# P A 1. **初始化**:
^( v& b( l" p - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。* Z5 d) d v& [% ^: f2 I5 u9 ]
- 初始化一个空的优先队列,用于存储待处理的节点。
% U w1 U& M! a) O& }
6 Y" Q& X7 y0 S* D1 i6 m+ { 2. **处理节点**:
" i- M% m1 |/ l Z4 h! `# { - 从优先队列中选取距离最小的节点作为当前节点。
- z5 P/ m F" d' L! Y - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
2 Q! l; ^" T V% |& p& ?/ I
1 U% F* I4 u3 J$ F* d: {% f 3. **重复处理**:
% V! I+ c. w) W9 Z& u* F, T3 Y1 d% h - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。$ [1 G2 |) ?9 @" P4 r
4 b/ e- q% j7 K" P- Q& Q/ u+ A: C: |
### 示例代码3 f( T: L4 l: C
$ [6 p, `& F0 Y' \5 s- K
```python
& [2 Z% k9 a8 |6 t$ u8 T import heapq; X# r+ r* l! d9 H( Y
% Q1 c7 M; A8 O; a def dijkstra(graph, start):
* A% w9 N( s( P0 Q # 图的表示为字典,键为节点,值为邻接节点及其边权) f! f1 `0 R7 s. ]7 ^* r; i4 {0 \
queue = []. r! W; {5 U8 f7 t$ P
distances = {node: float('inf') for node in graph}0 [) N. {* f- B$ a/ G
distances[start] = 03 p1 h# c1 R) s+ q4 e3 d. k
heapq.heappush(queue, (0, start)) # (距离, 节点)& `8 p* b$ U g6 s+ u+ \
5 F! E% e, e' H( x, W
while queue:' ]& M1 R* T7 h1 O8 ?
current_distance, current_node = heapq.heappop(queue)- B( X) t# T$ a8 c/ e
& q |0 ^1 b- D3 \6 u( O. w% ?; k
# 只处理当前节点的最短距离
3 Q) G; A9 F( S. b) `/ W- m if current_distance > distances[current_node]:$ }( k8 P; S; \5 e" L# Z3 k: d3 P4 d7 P
continue9 q* X2 t0 M0 c6 G9 y
8 w4 `/ p1 X! M6 _
for neighbor, weight in graph[current_node].items():% p U) s% F6 P0 ^/ D
distance = current_distance + weight8 `2 z; u' h# w2 w3 h6 Q. w2 ~4 t; g
* }9 h/ ?. P+ { # 更新邻接节点的距离
% v" j8 A( ?8 T- o7 A6 _ if distance < distances[neighbor]:4 Y( m) W+ m' w2 q0 U
distances[neighbor] = distance
( E# y- {! `; r* Z3 w8 _9 S: U heapq.heappush(queue, (distance, neighbor))- m9 Y3 O& `' Y
' s1 k- f' D! v6 t return distances
6 W% a$ [* r4 u$ d
! b* i! y, x7 i8 {/ d- | # 示例图! w. _5 B9 T ]
graph = {
; g7 }( i* F8 g: W 'A': {'B': 1, 'C': 4},
* T, K' L2 ^: N$ _, K4 V9 ] 'B': {'A': 1, 'C': 2, 'D': 5},% v2 R% e, W+ s2 H9 T3 b, a
'C': {'A': 4, 'B': 2, 'D': 1},
; d$ |4 a# @: T 'D': {'B': 5, 'C': 1},' z! ]- C8 _" w
}# w7 F* f) i: @4 e( f+ e
% u2 b1 [3 u2 Q, }& R- _, B% R5 |! p
start_node = 'A'$ K* h) J& e, S" v& X
shortest_paths = dijkstra(graph, start_node)& i4 V* D0 i* _6 U
print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
. X5 `+ z* e2 u ```
g. A; R& J% Y ; l- ?6 K/ y8 ~! b1 m' z
## 2. Bellman-Ford 算法
) I1 A! O3 t% h2 ]+ @5 Z5 k6 B8 d5 @
3 ~% {2 C6 q( o' C Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。
8 s* T8 X, G _! B! S. W, P ; z( ?3 F, u( J+ t, H
### 原理步骤2 A! Q+ n% O2 ~; _
5 Y6 s# j+ n9 e' w5 j4 y
1. **初始化**:% ~! Z% p' R( T) b- K1 v4 q- o9 Y3 q
- 设置源节点到自己的距离为0,其他节点为无穷大。
9 x7 @% D$ U% z2 b
$ V H+ Y, _1 V2 j: L. _ 2. **松弛操作**:* r' E6 ^+ @7 t- ?6 \3 {
- 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。
* d' W2 W. g7 E& f * A7 J2 x$ i a5 f5 g+ l' B4 U
3. **检查负权回路**:+ K* P8 Q/ l) R) _) j; T) ]" ?$ G
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。: |# s- y8 j) S
8 h8 G' r9 P/ C+ F0 F2 h+ Z6 w- f ### 示例代码, ^- C0 M1 ~# B8 H1 Y
7 L* J. { d( a- p8 a, f- |
```python
9 S! c b( Z, ~5 ~& j def bellman_ford(graph, start):
9 h8 i; \3 K* a1 Z2 W% P # 初始化距离
8 i! H) _3 |" e8 P% A" | distances = {node: float('inf') for node in graph}* V- S) s$ q# Z6 a% F F: K; C
distances[start] = 02 t2 `) V8 N( X+ s, ]. D( z1 }( p
" P* H! H3 N$ o1 w/ f
# 松弛操作# N/ R" G9 w) g# h% T8 l
for _ in range(len(graph) - 1):
1 d5 p& q5 S1 r! X for u in graph:
: Y6 U# P" G8 a5 R for v, weight in graph[u].items():
& |9 h) I# N+ D0 B/ }0 Y8 v# g if distances[u] + weight < distances[v]:) t' F, f0 M& T- A7 r: ?) p
distances[v] = distances[u] + weight
) c s" k- }/ [& r0 e' ]5 P' ~7 e ( E% |! R3 A3 U3 n, ?. B
# 检查负权回路
3 {+ S& |, g. R7 `. t" ?1 Z for u in graph:
7 H6 j, [. e8 O for v, weight in graph[u].items():' x9 ]4 B2 A S @$ j" o
if distances[u] + weight < distances[v]:9 y+ c1 ]1 w9 j3 D5 ?$ W, T. e. D
raise ValueError("Graph contains a negative weight cycle")1 U/ l# H b8 L/ M' e1 }3 n
9 E0 S* `, L/ V. u& B
return distances6 {. r3 U0 P! u
& l9 f$ x( }* u# S; \! k0 a( w # 示例图(带有负权边)
# c' Y8 `3 y Q graph_with_negative_weight = {. l9 K- N4 {( ?0 q) H$ G b
'A': {'B': 1, 'C': 4},
" o: a0 y; M* Z0 R* e4 o! s& n 'B': {'C': -3, 'D': 2},
1 \9 G8 I$ R. Q6 e 'C': {},. C- k' k3 e2 W q/ y* ?& e
'D': {'A': -1}! H9 K$ X" K$ u* D+ a
}" x1 n- a& ^5 }# J
6 Q4 y. y; |2 b( F start_node = 'A'0 u x4 v# k+ D9 O/ A
shortest_paths = bellman_ford(graph_with_negative_weight, start_node)) w7 D* }( T5 Y$ I" |+ \- [
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}. {3 I8 X/ {4 f" @0 d5 V
```
& M7 t9 b9 ^0 z% V9 B4 ]
7 j, ?! P0 ^9 T2 W6 n5 w ## 3. Floyd-Warshall 算法/ v/ n2 [: ]5 s z4 P; k! a& l
: x6 k4 k2 d# ?; m6 S
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
2 F5 n% }# X. U0 z& |
2 G2 ^+ w/ n# { ### 原理步骤! i% d& F" N( |8 M8 L" e9 `' J. c
0 c( j/ U- f8 s% v/ S5 _% w 1. **初始化**:' n F( @" h( I7 U. { ?5 r4 S
- 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。
7 L' W% @ O9 R! f& F6 ~& F 7 C& _$ i* B) s; I/ h( u
2. **更新路径**:
2 [9 u7 X# ?. s( x! @* Y - 三重循环遍历所有节点,以每个中间节点尝试更新路径。3 x. q- U+ v/ B
! r$ f- v* J) B, Y
3. **输出结果**:9 w% Y/ N2 i) z* _7 _
- 最终得到的矩阵即为每对节点之间的最短路径长度。
, N K/ b7 d7 B# x5 H
4 u' Z' t3 m' _' I9 [ ### 示例代码
}9 W1 v& D- `8 ^- n4 A
, z) H* \4 g7 ]1 ~9 f' H' X ```python7 F& t( F; Q% }6 l
def floyd_warshall(graph):
) e9 Y! ^( G e1 q # 初始化距离矩阵4 Z3 a; { g* d9 I
nodes = list(graph.keys())
9 D S' s) e, E0 G distances = {node: {n: float('inf') for n in nodes} for node in nodes}
3 t& j9 }+ ?6 l/ ?0 }5 Y) U
: _* |$ O# p# L# }8 E for u in nodes:
0 t% h* X5 U+ n, c* \. ?0 T0 G distances[u][u] = 0
A$ S* Y; j" G for v, weight in graph[u].items():; u+ k* P" E- J, z" J9 S9 e b* Z
distances[u][v] = weight- \5 T4 g' J& q/ T0 U- s* N3 p
, x5 y, q" n `* u0 N
# 更新路径
: s' y! u; o8 R' K5 f! [7 G0 f for k in nodes:% z a4 D5 l8 L, U$ A, ?4 y; A
for i in nodes:
C1 Y4 r8 {$ o% j. F1 i8 V/ f for j in nodes:5 ]3 Y; I4 D3 U3 R; z
if distances[i][j] > distances[i][k] + distances[k][j]:/ r: F+ h& Y3 H' f+ m' j/ z$ A6 Z
distances[i][j] = distances[i][k] + distances[k][j]
' }4 x) D. P! I/ n& K
% F7 G: R/ m" {- D6 N# h9 g return distances5 @6 G0 A& x9 w5 X+ f; Q" y# j% G
$ W( O+ n6 w3 f2 a, m3 q; ^ C # 示例图(可以含负权边)
$ o( @# d. s: _ graph_for_floyd = {* c& X, N e1 a$ V+ h+ z
'A': {'B': 3, 'C': 8, 'D': -4},
8 D" O) z( Q' l: C3 j+ f5 [ 'B': {'C': 1, 'D': 7},0 o8 c A- y6 l
'C': {'B': 4},
" e7 Z2 H+ d, ]8 Q& \' x o% n 'D': {'A': 2, 'C': -5}" D) e" t, j9 W! r5 A6 H4 c
}3 Z0 E/ h# Q2 ?* L* [) v
3 x! O: `. U, F# f0 f shortest_paths_matrix = floyd_warshall(graph_for_floyd)* x/ [ R4 Z" A. z/ @+ U5 ~8 R
for row in shortest_paths_matrix.items():
$ C" T& F4 g' H9 `) P1 ^, t print(row)5 P( H' O6 a7 D! z! W9 _
```
0 S4 G5 H0 ]: L $ {8 L3 q% B" p' T: ]
## 总结- z8 `1 a+ |8 `
1 S5 t9 Y( c7 D: k
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
% q1 H+ f; x* ~0 Q; e8 z& Q. ^! l - **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。# a9 H' T% i }3 T
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。
' B5 d2 Z" t- ^1 }$ g
9 |# w' { H% F% E 不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!3 s1 R& G# M& R
3 n- }" Z# D& |
5 c/ L! t( k1 O5 x4 s* p
, \: Z6 b- V7 @& {7 j, ^% O3 y6 j
zan