在线时间 481 小时 最后登录 2026-8-23 注册时间 2023-7-11 听众数 4 收听数 0 能力 0 分 体力 7858 点 威望 0 点 阅读权限 255 积分 2946 相册 0 日志 0 记录 0 帖子 1177 主题 1192 精华 0 分享 0 好友 1
该用户从未签到
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。& ~8 p/ v N0 y2 r; B
: m5 `; T: f) K' B Y
## 1. Dijkstra 算法. e" D9 H0 F! x
+ }+ G: ]. n1 K- f Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。4 F% @0 Y5 O" Q( h5 a
( V* i0 J$ B8 `' h" x
### 原理步骤
' A* x5 m) e# p4 x
6 Y: y. k3 |: \4 Y" b0 f 1. **初始化**:# }) p1 ?2 }; P$ _. {
- 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。3 ?1 ^9 g( z, h: Q. X0 `
- 初始化一个空的优先队列,用于存储待处理的节点。: N# G' {% x" u0 B: q
1 }+ C; T# L2 _ i. U 2. **处理节点**:
7 I! G0 g' }( r3 f/ G2 s$ F7 T+ f - 从优先队列中选取距离最小的节点作为当前节点。
9 r$ i& z1 i& h' Y& P6 R' q* j, k) S - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
# @* m1 v# g- i4 J
- y2 O' @) a/ |1 H! a, B3 z' W 3. **重复处理**:& P9 V: L6 u. j+ Q, b, h
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。
% p/ j. r3 H0 [5 \4 E3 |" o q
9 ^/ r3 A' k q" L3 [+ Y ] ### 示例代码 A6 X) t7 K" v% N) S6 x
% V: s. D7 v! |+ R ```python, j% }1 h8 E, F7 ?: y% E' {( R
import heapq
8 {) A- [: R" Z; q+ C- q; C3 @ ) u" l: R/ j6 A
def dijkstra(graph, start):
% Q# U9 i+ ], ~# F # 图的表示为字典,键为节点,值为邻接节点及其边权! o8 w; E' C' N( ~% s
queue = []3 R; |; L+ D! h( C/ l/ C
distances = {node: float('inf') for node in graph}# Y& y1 @) }( @: ^& t
distances[start] = 0
8 u+ N" |1 b2 C9 S9 s) `: e heapq.heappush(queue, (0, start)) # (距离, 节点)# k+ Z+ A1 @7 G9 Y& J0 s
4 ?% Q. f1 z. O
while queue:' B+ I y% d. }) s# r+ \! L
current_distance, current_node = heapq.heappop(queue)+ r6 h2 C9 {8 d7 j! ?
( n( x; h7 G' k- X* M
# 只处理当前节点的最短距离
+ ?# b5 J( H* V* k! F2 y8 _ if current_distance > distances[current_node]:
, {& C, i; M# } continue2 L3 D# g2 a) ~; w2 d
8 }# m6 t* d4 h" _ for neighbor, weight in graph[current_node].items():% p6 v2 q% T8 ^9 p5 M; ^
distance = current_distance + weight. ~( k2 s; ^4 F' W8 A* O
/ p3 u/ Z2 ?5 ~# K2 K$ Z5 d& R # 更新邻接节点的距离3 Q6 K) @, ~$ I1 E; N' D3 f
if distance < distances[neighbor]:+ ?+ V3 \# l, k0 f* W
distances[neighbor] = distance
- Y8 H B4 k+ n$ |% e/ T heapq.heappush(queue, (distance, neighbor))
9 P4 F( }; j! `2 r/ l1 O' p0 |! v 4 j' i' o0 y" G( ~
return distances
; v6 `0 ~2 F3 w* K9 `0 m6 S \
7 u/ v! { f: D3 L1 O9 b0 _ a5 D # 示例图
& s6 ]# ]' Q& c& g# U- s graph = {
4 M0 P- E0 f2 L 'A': {'B': 1, 'C': 4},
3 ~- g6 f/ y" R0 f Y 'B': {'A': 1, 'C': 2, 'D': 5},* A z( F9 [# q
'C': {'A': 4, 'B': 2, 'D': 1},3 M2 c" Y2 f: |% f8 m D
'D': {'B': 5, 'C': 1},
+ b; E* U1 c/ b }6 Q: z& k/ R! a0 _% z% h( `9 X
" c/ E: x1 B- G
start_node = 'A'
2 k8 _' r! g/ J/ v( U shortest_paths = dijkstra(graph, start_node)6 s' u) W# Z& f' O! _
print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}/ {9 q6 D3 A; r( S
```' r V# H9 O% Q3 ?, V* S5 q
8 B# A Z+ i0 F5 v7 `) x2 [: a
## 2. Bellman-Ford 算法
) Q2 t2 B# V; E% N% F( {& o
2 W$ j. b" B5 _. E3 y2 q. o Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。
3 J! M$ T7 u6 T" X3 i& r% [
& D+ v7 \9 }, D ### 原理步骤1 J2 ~" l% U `' A7 d
$ m- W2 b* q0 B1 Z" y. o! @' ^ 1. **初始化**:2 b- F. D; r0 N1 M) L+ J6 d: \% f
- 设置源节点到自己的距离为0,其他节点为无穷大。
* t& J0 z1 ?2 J) i7 m8 s: w
, T; C$ e1 x" j& a# B1 @ 2. **松弛操作**:/ k( C" p; \( [
- 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。
+ ^6 V; ?. b0 M% ?; R& T2 Q 5 A: p Z3 p! c
3. **检查负权回路**:
0 d# [$ L4 Y W: g e - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。3 `' U) |) o) `
+ r4 v1 a8 `' b$ `; Y ### 示例代码4 h Q9 Z D w" h; \" Q
# A! `) L6 M$ e) f. o7 c
```python
0 b& R6 Z) x( q: M, q def bellman_ford(graph, start):
6 o: Q3 t* B/ ]3 Y$ z) g! d # 初始化距离$ L$ h5 w& S: P, ?/ @: O! \' j
distances = {node: float('inf') for node in graph}
; i2 Y0 [3 P# e$ b distances[start] = 0* p# W1 c O0 n* t
) p/ e0 ~4 P: k' U
# 松弛操作" a; G9 c- ^4 G/ l+ n+ Q7 b5 }
for _ in range(len(graph) - 1):
5 n( Z0 S$ B$ Y& g; W for u in graph:, z+ S. g) x4 s4 K) S
for v, weight in graph[u].items():0 F* ]& w B& |' K# x; [
if distances[u] + weight < distances[v]:" X. W2 D- J8 I* d F
distances[v] = distances[u] + weight$ g5 n3 I( c# f, t. ~
7 ^) y+ ]+ ^7 D" Y# e # 检查负权回路$ L, j9 Z5 D6 I) @2 R) e5 f
for u in graph:$ P" {# G' I/ Y. U! i, S3 f) D
for v, weight in graph[u].items():
3 I6 E, D( Y x6 }( B8 O7 r if distances[u] + weight < distances[v]:" }; O8 l, ^. e1 n, e$ G4 a( g
raise ValueError("Graph contains a negative weight cycle")
) u1 f* Z* u! m- B
% G! K& ]" \5 o; C return distances
2 F( U7 A9 |$ S6 S s' J, T( ]- V" q" |- z
# 示例图(带有负权边)
0 U% E6 b: T' I( x$ W graph_with_negative_weight = {# T9 N- e9 g$ \
'A': {'B': 1, 'C': 4},
6 t' p, M! V8 J. S W" | 'B': {'C': -3, 'D': 2},1 m- t) x' G$ G& T7 O. [7 q0 }# C
'C': {},5 E9 n) J9 i, h
'D': {'A': -1}
# ~" d! A; Y* y5 C O }5 E7 P1 I* n8 r. y- I# V0 B) |# s
4 A3 \) l6 U5 B, ], S# [
start_node = 'A'3 y; Y. L$ ^ S, h3 [
shortest_paths = bellman_ford(graph_with_negative_weight, start_node)* @- ^, B/ I; e* O4 M/ ^/ `5 J& f
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}8 c( p9 }- ~" S( W3 r: w
```. t& P: ]2 p& w( Y$ L: h: o
# o7 E& X" x1 k# J D ## 3. Floyd-Warshall 算法- c2 k l8 s, H% r. y
# F( q! D/ b! I7 b# |7 n$ F
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
1 [1 |* D4 z: k( }- u" l; I' m" p 3 \$ U _2 V2 N0 a- J
### 原理步骤6 |; F7 p% M5 c- B+ d8 L! S
0 F% }. l" X" z+ } 1. **初始化**:
2 w- [5 w1 s5 j - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。' J& b2 ?$ S2 Y' r
1 T' ]5 @6 ?: \/ R6 L! D& M( e1 G# o
2. **更新路径**:
% V9 Y7 d1 M( E) G5 l - 三重循环遍历所有节点,以每个中间节点尝试更新路径。
' y7 E# o; {# F- r& O
7 P$ d# H$ N: l8 x% k 3. **输出结果**:
7 {# L% v! j2 B6 M - 最终得到的矩阵即为每对节点之间的最短路径长度。
# `. e3 A2 S: e' y# F5 }$ V. v ( h% x7 c0 J: L3 q% m* n C$ W: h
### 示例代码
; g9 a* b `( a. @4 u% W) O, K, g
, g! A K6 \% L8 k _! D0 |5 t! i1 _3 l ```python
$ R3 J1 g! k0 F& E: R5 Z5 K: E+ U8 M def floyd_warshall(graph):
+ e3 y% o9 r) K4 W' V7 ^1 M. r' n # 初始化距离矩阵" k( s3 b( _- S; m3 ^
nodes = list(graph.keys()). Y6 ]; {9 q4 W, A* }2 k/ @9 D9 X- g
distances = {node: {n: float('inf') for n in nodes} for node in nodes}; j- s+ Q: m6 i( O, G+ A& j
& ]/ G( m9 ^! V1 M5 e" f r1 v q2 y
for u in nodes:
! k4 }+ ] w- w distances[u][u] = 09 C1 _: V4 V# @0 s7 _( e# u
for v, weight in graph[u].items():
# s% p7 f( Y3 f( M4 R# \; F# D% E distances[u][v] = weight; |8 q; J5 j' N" [* h. y4 L
6 g7 [$ \# k9 z. q5 L' k; L) \
# 更新路径; T( c8 R5 D8 W# w k" ]/ J! b
for k in nodes:( y0 F1 M- f$ T: S8 Z4 g1 e
for i in nodes:5 y7 F+ J9 ^5 L( o: _& ?) A
for j in nodes:
: Z. f8 g T P" W, [ if distances[i][j] > distances[i][k] + distances[k][j]:
( ^/ J+ L+ y, a" j6 h) H distances[i][j] = distances[i][k] + distances[k][j]
2 s- g: N4 [7 O" X, F. ~ # Z5 N3 s1 q: t( V, ^
return distances
0 M, h( A5 L( \% r+ ] 5 B% j* a9 P- o" L) y. U0 W
# 示例图(可以含负权边)
6 Q6 K* i8 k! T; C/ \; n5 J3 x2 S graph_for_floyd = {
# _( C/ Y0 f2 I4 t 'A': {'B': 3, 'C': 8, 'D': -4},
- W( a" _9 \% k) Q. ]9 [ 'B': {'C': 1, 'D': 7},
9 d% U) |8 T. N# E 'C': {'B': 4},
" w& S: H4 z6 l% |; ?! n 'D': {'A': 2, 'C': -5}
7 t* [- v7 y) r# ]& H. r }) Q8 b, z+ B6 h
# y! x1 C w4 H5 y2 I5 l- U9 W1 s shortest_paths_matrix = floyd_warshall(graph_for_floyd)8 X. x; u, [! t3 `, c* c: w6 `
for row in shortest_paths_matrix.items():5 D* e3 K) R' v9 f: }+ M( x/ e
print(row)8 H% p$ X, r' ] z- M
```% u7 j h7 o% `6 W5 ?+ {* [
# u9 B( |& f6 ]: F4 ^& y ## 总结3 C' v; T. V/ H' e
+ R2 C9 G. l/ J. L$ @4 K - **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。- m! D" e0 ]6 v
- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。$ N8 y( C: y: m
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。0 ^" Q& H( n( V9 c w
; Q6 N# K* W# s% u" i 不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!
! `: N$ |5 S6 A) U
8 v% |5 ]* P3 t) R* l P) q8 k: l) f ] : ]( u$ `; O& Z S) ~
, G$ l+ {2 q- S) ]4 v+ I0 L; k0 W
zan