- 在线时间
- 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 算法。下面将介绍这些算法的基本原理和具体实现。7 J! g. E) V( M6 z0 j
# w" `) S; K4 S/ o## 1. Dijkstra 算法
- V, H* A& Y5 [8 V% U9 ?
1 e6 t L9 m$ ]5 s y6 s2 p6 z- J W9 tDijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
; A- R C. H0 {$ V6 T' a7 f% i- h1 a* \: b
### 原理步骤4 S* U- o- }9 w% E% m7 U- C4 r0 v. q
8 M* u; ^0 y0 D# Y! r: k% m6 ]
1. **初始化**:
! j/ ]- F ]" n$ r1 h( _; H: b. X( A - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。8 d% Z! F9 r; z2 C6 i
- 初始化一个空的优先队列,用于存储待处理的节点。( E4 F8 q1 G% I# s
* ~( Y7 U/ d5 ?6 A# R: I& ?9 ~
2. **处理节点**:8 @# ^4 X% W( t9 Z* h, i; y; V
- 从优先队列中选取距离最小的节点作为当前节点。
# e) v4 t& D% S6 {( a& _9 C - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。* Q, H6 O4 x! h2 U8 ]
: N' g# i+ ~* g' ^* C/ X* C3. **重复处理**:) r0 d' T. M1 g
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。. W- w1 B# l# _# _
5 k+ G* e: E& M' S5 { R2 F" e& b( b### 示例代码$ b, s) [% }- b( D
% v) j7 l" F6 S7 X8 R) v* P
```python4 N9 p* j5 R* Y" [& P$ O2 ]
import heapq
7 s# {) S- a4 q% P
1 [- `) m$ a V5 @2 \5 k9 @9 ndef dijkstra(graph, start):& I! x4 a1 k6 c) Y& T4 n
# 图的表示为字典,键为节点,值为邻接节点及其边权
; U% Y8 ^6 v+ m6 L( W7 h" W queue = []
4 Q$ |5 y& t$ w3 ^* T0 ~ distances = {node: float('inf') for node in graph}3 F% o: P( J7 _% p% b4 f
distances[start] = 0
* i9 J3 ~1 T6 j% t heapq.heappush(queue, (0, start)) # (距离, 节点)
; x+ G, t) C( _+ X. G, B
$ Z. @( P2 l3 `$ ^0 e while queue:1 y, D; l7 G) i5 W
current_distance, current_node = heapq.heappop(queue)
% ?; R7 G e0 b* T
7 K7 |8 ]. O5 D; b5 }2 f # 只处理当前节点的最短距离0 E3 c, M: u6 i* S' c% G
if current_distance > distances[current_node]:
6 x* E0 f( G7 k& u) B' D; k continue
`# W/ N& L/ }( H' Y: D4 ?8 S5 P
`& f8 v' Z' \+ K for neighbor, weight in graph[current_node].items():+ _: {& N+ p2 _# C. z
distance = current_distance + weight
, p' ]2 P9 @0 a M/ [/ |6 }7 V/ r& o0 k, z$ g9 G3 O+ l
# 更新邻接节点的距离% _* j5 r0 V- T) q
if distance < distances[neighbor]:
% M! |, i1 z! v8 e- t' ^- | distances[neighbor] = distance* _% a) ]" Y4 F0 T3 p2 c
heapq.heappush(queue, (distance, neighbor))" W- A- u" R/ F0 c
8 ^* _, s4 u/ P$ F) w* d7 C return distances
0 Q* f: c2 C" Z5 N8 N! F% T, G' l: T* v5 o$ y; W. x
# 示例图
* u% H9 c) o$ q6 v# R3 h, h+ egraph = {
2 C7 l& m" u2 u; g' b1 h 'A': {'B': 1, 'C': 4},6 Y5 F) q/ o" ~
'B': {'A': 1, 'C': 2, 'D': 5},
- d* S6 q5 Q) w. g8 b; h( p 'C': {'A': 4, 'B': 2, 'D': 1},: a$ i6 ^8 o5 x5 G4 F1 q' ]
'D': {'B': 5, 'C': 1},: X/ W1 q5 @- }: _! P3 J9 a
}
) `) M1 {) g( L* {; Y# N& i3 H+ p, ^$ W2 p
start_node = 'A'
2 S( v4 U7 _% l: r( z9 {shortest_paths = dijkstra(graph, start_node)
7 g) \+ C; Q0 }print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
% O9 ?4 Q# m3 f# O```, I# w" Z) E1 t/ D
) G( ]( |$ g$ `, y8 }
## 2. Bellman-Ford 算法( m+ I8 c9 w& s6 W) x' \
! j7 M% ^( U7 m2 e0 }, q# H, R3 W( i
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。/ s9 b2 C- d0 z; _1 T7 m
D \" Y, |& S7 g, j$ E, G### 原理步骤
2 T4 R1 r7 ^9 K2 L5 i3 J- e
4 Z' F$ o, h7 d( o- b, `5 n: K2 F1. **初始化**:
' I( R( b) w6 s# \* \ - 设置源节点到自己的距离为0,其他节点为无穷大。$ S2 [5 h( V. {# E! G
& ~) _( M5 F& ?! G3 q2. **松弛操作**:
1 j7 h7 f+ y; c* V - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。: I' E) f2 }2 b* N
* c9 @. g, }+ L/ S0 p; S7 Y1 m% X3. **检查负权回路**:
! b9 i& t+ |- }) `4 u/ Z' O% n/ M X" B - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。
# V& r0 y& P6 L: E9 F* p% S
: Q; t+ g* N$ L0 A+ w, \### 示例代码: q: t0 O- ^/ h
5 O. J( q! [$ j```python( L# U3 e5 e% X6 p! G) L0 T
def bellman_ford(graph, start):# i2 I- U3 f* z, N& N+ i1 D J9 l
# 初始化距离
) f6 b, }" ]0 K distances = {node: float('inf') for node in graph}1 Y1 s; l! P4 _" v. V9 \+ D1 w
distances[start] = 0
+ r5 L+ _+ j( i3 B5 n0 c& f. ~; D
5 w+ h% D6 O' f2 ~ # 松弛操作
5 J- S+ {- ~# Y0 J, G' R for _ in range(len(graph) - 1):
+ J, z( ?* Y' d' [( X0 q; w1 M9 F for u in graph:% B% d( I1 R4 h4 G: T& Y
for v, weight in graph[u].items():9 J0 G8 v6 P7 x* y# t8 v
if distances[u] + weight < distances[v]:+ K7 Z* ]9 V4 @/ ^6 G
distances[v] = distances[u] + weight$ [; W' e5 T. _# O
' [" R4 Q' e( Z; w
# 检查负权回路1 Q/ u$ Q2 K' p' s: V/ K' ]
for u in graph:. K& r3 l4 y G/ q% Y f
for v, weight in graph[u].items():2 s U' H% T) t: m1 B
if distances[u] + weight < distances[v]:4 L) z1 l6 m8 ~1 b( c! L
raise ValueError("Graph contains a negative weight cycle")
4 Q% y+ O: q# D+ M% g
# }2 v8 R3 W& a( \: w" c return distances
# d% O' h! N1 v) l6 k2 ~ E8 `
1 O5 m+ r0 l) M* O" }5 u: A1 f% f# 示例图(带有负权边)! n/ h7 R' A/ K2 B- x8 h
graph_with_negative_weight = {
% q# u. ~6 Q4 a; i5 h7 V& u 'A': {'B': 1, 'C': 4},
1 B( g* F8 S+ U$ Z* l' X 'B': {'C': -3, 'D': 2},2 S% B2 T" ~, I' }" G
'C': {},) m( b$ u# a) U- d/ |5 V9 x; U
'D': {'A': -1}. y/ i% o5 Z( M% b% T3 [9 h
}
4 X6 z, n( M: G. K( B
( B0 k3 B0 I0 \6 Zstart_node = 'A'
' o8 S( s9 E* c, ~shortest_paths = bellman_ford(graph_with_negative_weight, start_node)4 ~) c3 v* X4 X! U9 W8 R
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}
1 B6 _3 |/ ^& g& _8 @```
' A7 V# ^5 `5 R8 S6 ~: J
# c, ~2 ~. ]: E; e& t/ d: L& B: t$ o## 3. Floyd-Warshall 算法
O6 a$ ~2 G& l0 h D7 Q/ Q" W6 }! {3 A2 q
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
+ I% Z+ l1 R( U, A; a9 _8 r# ]7 Z' V0 n2 I6 f# d3 j
### 原理步骤
! n3 u7 X9 v% F- q4 g- f! H( D* Q. t6 F, n( L1 u. i
1. **初始化**:
& S6 V6 \( _% P5 x; V. Q* X* [' q! j - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。
- P5 D1 X2 P2 D9 a$ n
2 s/ M/ ?! I4 e2 Q% b8 Q2. **更新路径**:( T* c3 B% s' B: {: p# S( v
- 三重循环遍历所有节点,以每个中间节点尝试更新路径。1 P% ]. Q1 J& e K1 _5 t: A
5 q8 R1 P% [. _* ]) Z
3. **输出结果**:
0 r3 V+ X$ i& M4 z" d - 最终得到的矩阵即为每对节点之间的最短路径长度。
7 }8 ~! X; e1 G' t
; q& y3 q- g; Y ]7 E. X### 示例代码
7 r4 g1 P3 C! s! g3 K$ w
, ?9 `9 \9 O: r: f```python
. ^) a. C! S* ]def floyd_warshall(graph):8 ?) Q4 h8 N8 S( G3 ~
# 初始化距离矩阵8 W# Y+ s* i( e
nodes = list(graph.keys())& |5 l8 W' B1 W6 U7 w! A& ~- X! h
distances = {node: {n: float('inf') for n in nodes} for node in nodes}
$ D# d8 M5 H. [( j+ }. N9 J/ ?5 D* b& e$ m6 m3 m. ?
for u in nodes:
; U6 Z+ N8 Z$ v( L distances[u][u] = 0
o# _1 w: T7 C) j" ?3 T for v, weight in graph[u].items():8 X7 h4 n9 Q; k5 x2 |
distances[u][v] = weight3 e& w* m" M! `; j
# u$ V- b& H0 g# `! R2 r3 X
# 更新路径
1 u U$ I @% g& G. o9 W for k in nodes:
, x- P( W2 _5 } z5 ~ for i in nodes:
# g- ^ z% K) X) H* r t* R& B for j in nodes: V- L8 B m/ O* u# _6 ]7 |
if distances[i][j] > distances[i][k] + distances[k][j]:4 T% _" T2 {/ w& U* p
distances[i][j] = distances[i][k] + distances[k][j]
% F- j, \! R: R/ e& z# O8 `* v$ o+ J, F q$ r
return distances
6 _3 X. o0 ]6 a. e% R$ o+ ~& Z5 I6 G3 n8 ^
# 示例图(可以含负权边)
. I4 J7 m3 k2 H+ Q# ugraph_for_floyd = {( m Y4 F F, G6 {/ N% |0 y
'A': {'B': 3, 'C': 8, 'D': -4},4 f; O7 N% p. ]4 y& d
'B': {'C': 1, 'D': 7},
( v, }" r* {7 m 'C': {'B': 4},
1 g# d) p' w! Q* o* Y+ Q 'D': {'A': 2, 'C': -5}
3 o- a4 G# F1 g5 o6 X, o& P. w}
/ o5 h: H5 f; C* @. ~' O3 p3 M [2 W2 p
shortest_paths_matrix = floyd_warshall(graph_for_floyd)/ N% B8 t/ A1 S! t" f% S8 ~7 i
for row in shortest_paths_matrix.items():
2 _$ M* `1 a6 n print(row)8 j. x( _9 `- ]6 a3 t/ e1 j- i6 w4 y
```
0 p" `0 j* I# t
7 J0 J) `3 G* P2 ^5 D, Y7 B% ~; S## 总结
2 Y' E6 J) t5 m7 w/ |4 I3 v: R9 X& l e0 {0 `* r
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
! c, b# m6 k# `, G1 Z/ c6 A, _- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。
, l) w' k+ `" @! e! U E1 f( i( V& b- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。) G4 t' ^: v% ?9 K8 e, {
# N& g- x& A K# {5 i. m# z* @
不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!- d/ E- |4 _, E9 s2 W
1 H2 e6 c2 O6 A* f" d3 w; h; ?1 ]1 h1 ]9 V- V+ h4 ^+ i8 [/ y& j
# S0 @4 o8 k% l y& M |
zan
|