- 在线时间
- 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 算法。下面将介绍这些算法的基本原理和具体实现。
: T0 Z9 C4 _7 g* U( {/ s- D! O! d3 P& K1 g6 V
## 1. Dijkstra 算法
# e+ t6 j: n! Q5 m7 u1 N' Y; e: f8 _
. k8 [$ L) q) e/ V5 }Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
_6 r, x+ N! r# c" p' L, ?1 X. Q1 I( h D
### 原理步骤
# ~8 o: U A) R% {$ f; d* c8 T/ R& ~8 X1 j5 T
1. **初始化**:! p+ Y3 Q7 Z3 b. H) O3 k
- 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。
' O4 t0 h# V. I" p% N1 f - 初始化一个空的优先队列,用于存储待处理的节点。
' f, q5 o" S p! V: k
/ Z6 e% s4 m# P( {( W$ H0 j, r1 ~2. **处理节点**:
! B( z9 ~% e/ g( o) u7 p" s5 [" b - 从优先队列中选取距离最小的节点作为当前节点。: h- I: b5 Q0 c4 W! H
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
2 j% i# a# L+ d) p- E
3 g8 `" e$ S4 N+ l/ m3. **重复处理**:
! _1 ^" R2 n! p - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。4 s2 F/ P9 v6 E7 V' `- x
% `4 k; B6 j o2 A- M* f4 h9 \( {### 示例代码' ^7 J. s9 A1 y) k
" N1 Q5 t4 R6 N9 d, J2 W9 f0 q
```python& j: B4 [2 z: n }
import heapq2 G; ^" c5 T$ `% H2 O
8 @+ x( A, B( n6 }* Zdef dijkstra(graph, start):
& ~: Y. Q. y$ U# }/ | # 图的表示为字典,键为节点,值为邻接节点及其边权
$ m! r8 o( t* I) Z+ l; o queue = []9 @) J- f- Z1 W; p
distances = {node: float('inf') for node in graph}
4 e1 W+ A" V6 |) Z distances[start] = 02 }9 X( W% B, r$ K8 d+ u
heapq.heappush(queue, (0, start)) # (距离, 节点)+ s$ t/ S; f" m9 N6 v
* v! A& E# l/ [1 V& A: E# X: i. H while queue:
8 W N- u3 }9 C- l+ [, D current_distance, current_node = heapq.heappop(queue)
2 I2 r7 }! U' O1 H' |& ^8 a: r8 |+ y9 w7 p( N- y. |0 K
# 只处理当前节点的最短距离" T4 V! M# V9 w# U$ g/ s% t
if current_distance > distances[current_node]:
5 t' a9 T) }! O: c f continue
# p' ^; ]1 A! G& ~; p+ W- M7 n
7 A9 ~' V7 h3 O- ^; B* S7 V; G for neighbor, weight in graph[current_node].items():- ^5 z8 v+ `4 m; k6 t
distance = current_distance + weight1 R2 K. y2 U9 r( N/ \7 |, l
$ c; n) P$ l& ]: G3 a2 z+ @, c+ y- b
# 更新邻接节点的距离! ?; m; p8 W5 ?9 Z
if distance < distances[neighbor]:
/ \! P2 L7 `# n% A distances[neighbor] = distance( E( o: N5 s: g2 [8 K+ Q
heapq.heappush(queue, (distance, neighbor))
) z7 m G* R) b$ p. y% I6 C2 k
0 r0 J$ c+ {0 M" j return distances+ a9 N; S8 A" p7 W2 x
. ]0 b2 |' q8 |: K0 y1 U& W# 示例图
& k: y' a# c! g, P, h; Kgraph = {
- B3 {: l# S2 `3 O2 U4 `3 X! K# [ 'A': {'B': 1, 'C': 4},) ]) s* N' v1 @, U' P
'B': {'A': 1, 'C': 2, 'D': 5},
- y; \1 A0 K$ c5 U8 [- C& T, x 'C': {'A': 4, 'B': 2, 'D': 1},
9 W$ `& ?9 u8 G K( d+ B* A 'D': {'B': 5, 'C': 1},
5 M0 d6 }, z- {/ R}
$ M6 R& z2 S" b& q3 x
1 u2 d* Q) X! X7 t$ o+ {, \6 Nstart_node = 'A'
8 A, @! @9 ~9 Y8 C; r1 w, [shortest_paths = dijkstra(graph, start_node)+ d$ f- T% x' I7 [
print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
5 r& M% j5 d' K4 f3 g2 r$ ]```2 \" k% ]1 v2 D+ F- Y; ^% k* L; r
2 M; h+ j& s9 t# z0 ~' G2 v: A## 2. Bellman-Ford 算法2 W! j2 j; y% e9 n
/ z- A0 B9 L4 S+ U
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。) Y% e9 J. K1 @7 A0 f. R! m( e; R
8 G4 r5 N; D1 Y7 K: u* i) P1 n### 原理步骤
3 H& E, q0 I( b, S/ r' m" s' c s1 V7 T/ V$ d, L! J6 W# e8 R8 C# v
1. **初始化**:0 u- P( U, B$ `: \
- 设置源节点到自己的距离为0,其他节点为无穷大。( d* N' m2 J( D( {
. O ]# r/ M8 Z; `3 U+ p
2. **松弛操作**:- w8 f& y! \. S9 Q" w- [' X8 C
- 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。
6 F$ v: d. ~/ Q, K( d4 C' c+ o$ e) }/ m5 a |5 k( _5 e' ~6 M
3. **检查负权回路**:/ ~, L( ]% B# w) q4 m; u
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。
% Y3 c& V: [# D4 C+ e
, Q7 w4 t& }' H3 ~. k) g) O### 示例代码
% E' H' ?, s3 @2 @. H9 ?0 `9 ^& l2 Y
```python
+ L: x* h/ o* B- G) l; p. ]def bellman_ford(graph, start):2 j( H( L5 j2 O a
# 初始化距离
& U; k- p( h5 _ distances = {node: float('inf') for node in graph}" G2 i$ Z; S7 `7 a* f- Y% a
distances[start] = 0
3 X2 h9 U X4 C# Y/ ]9 F# K; Q; A
& ]7 R. J2 g$ @, d+ y7 e # 松弛操作+ S4 J' i# o* O& {/ f2 L
for _ in range(len(graph) - 1):
7 e R- s8 v: b" y& A for u in graph:
1 G9 w% W# l$ }4 n1 d for v, weight in graph[u].items():
5 d- Y% l- w* z6 c8 B8 W+ z! m if distances[u] + weight < distances[v]:
0 k# ?% a1 h1 R- w: T- f distances[v] = distances[u] + weight
9 R& v6 B5 f& ]% u D/ A0 V% L, \! S/ f# j
# 检查负权回路
; e: U- p8 Q. p: F# g) `7 x( V2 @ for u in graph:, K7 `$ G) O2 `+ D# v, H6 t; m
for v, weight in graph[u].items():
# A8 U8 ?. X0 Q5 s1 d& J: N. f5 c if distances[u] + weight < distances[v]:
1 n1 A5 K7 |- ^* j9 c ` ~ raise ValueError("Graph contains a negative weight cycle")
8 N- ?# T4 _$ i7 {7 G
; u# {7 S+ i5 f; l return distances# @% o2 \) A9 w" y
* {0 @, V7 L+ [- V8 q# 示例图(带有负权边)3 L$ f" r6 ?1 v+ I. L1 y+ R9 q- F
graph_with_negative_weight = {
/ h1 m ^ z0 D4 T# V- n 'A': {'B': 1, 'C': 4},
7 ^8 }; K+ Q3 E 'B': {'C': -3, 'D': 2},/ B! {. y+ F- R2 B% e: g6 I
'C': {},
8 y+ O( W, \3 l 'D': {'A': -1}# [: K& }' J; F& N& d
}# H/ o! s# } C3 y
+ U: I0 Y2 ]" n* ]" f$ F1 ystart_node = 'A'0 i5 N/ y: q& F
shortest_paths = bellman_ford(graph_with_negative_weight, start_node): T0 {- t( K3 ]% A- t
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}% z6 u' X/ z. x6 s2 E8 |
```
! K2 H1 E* l) l$ W+ w( a' E" {5 w0 y7 P; M2 ^! I
## 3. Floyd-Warshall 算法! z# j) ~6 E2 ^) a- P
2 [% @9 ~) D' J, x8 M* q6 `3 S! JFloyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
* {5 L/ q; c, f8 |5 ]0 e) u
; \0 t9 A8 i) K### 原理步骤% f" ?9 k+ c1 F3 |
* U$ q8 h V8 r0 r w. n" g1. **初始化**:( a4 t8 ?/ h0 M( D! Y
- 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。
! w4 F/ O: D$ i$ f- C; e; _2 r3 u7 P* i6 r1 K+ S# ~
2. **更新路径**:
6 e( M1 H) }; i. ]+ N" B" e - 三重循环遍历所有节点,以每个中间节点尝试更新路径。
~. ^5 u2 ^5 n6 p# C+ Z+ M0 Q* g! k6 q! b+ J% C1 c5 N" r
3. **输出结果**:
, i. i) x! Y8 K. _ H4 L - 最终得到的矩阵即为每对节点之间的最短路径长度。
- F* l8 Q: t$ @) q9 s Y% z
6 c5 Y7 k% C4 ]3 k1 T### 示例代码
) I5 c6 l* S% C
+ v4 F& y% G' J, O ^```python
" H) V, e+ K. h7 D0 Hdef floyd_warshall(graph):% h1 L/ E+ |% a" O4 D! b
# 初始化距离矩阵
, J N9 {' f! p4 \/ f nodes = list(graph.keys())( ~) O3 E( ~3 P& |, ^8 ]
distances = {node: {n: float('inf') for n in nodes} for node in nodes}
V6 W8 {* c2 [& Z0 f9 X
( \4 j2 ^ O) Q9 c" P for u in nodes:
5 g8 x2 G) u7 k* C6 a; d distances[u][u] = 0
3 P; D7 ?; a$ c% e for v, weight in graph[u].items():0 d, Q- L% E0 g+ X, j3 f7 O7 X/ ~8 D
distances[u][v] = weight
" O9 j1 d- M+ I$ R9 V. C* x" T
& L3 f- n+ T; z+ u( }8 |" D: e # 更新路径3 T3 ~( t/ v% ^% ]6 r) G1 B1 G
for k in nodes:
# y) z( K+ ~$ u1 L9 n for i in nodes:) B% l0 ^: r3 L2 m5 p N' A2 ^( P
for j in nodes:
& O- L9 p) j' G5 Y; r if distances[i][j] > distances[i][k] + distances[k][j]:, }) L4 V5 A! y. j- x- J
distances[i][j] = distances[i][k] + distances[k][j]
9 w) @! X' |; S5 Y/ m- Q' e2 b0 _( E
$ r6 f6 }4 t0 U0 b I5 ?1 J return distances3 s. A. \2 x: t# X( O7 a
% Y, \7 n# A( n3 [; E7 K+ M# 示例图(可以含负权边)
# c) d( J3 {, B+ i; G% e/ ograph_for_floyd = {
/ N$ E# ?3 }& I7 d 'A': {'B': 3, 'C': 8, 'D': -4},
7 }2 K# s0 E( g 'B': {'C': 1, 'D': 7},
* `$ F- X: t: y- s+ t 'C': {'B': 4},
; G. V$ w0 _! a 'D': {'A': 2, 'C': -5}. Z1 ]% o: d8 o& Z) v2 Q
}
1 a: x# ?6 Y8 H
9 B6 C1 ^0 ]" l- _4 Dshortest_paths_matrix = floyd_warshall(graph_for_floyd)
) K5 e: x9 \" h+ K ofor row in shortest_paths_matrix.items():
! B+ H& m9 L+ U6 a K: N' D" x print(row)
# ^/ e1 k' I _3 W" s0 a0 m```2 j8 Y' x/ V' C4 B- G* Y0 K" f
( _8 ^9 ^" l" h
## 总结. B: v {/ Q- s' C- `6 S! B2 I Z
9 D4 ?9 z4 @& d W
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。4 }8 n* q* z7 k! b' {4 r2 A5 @3 w
- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。
7 n! R+ ?, E: r7 `- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。! `$ D/ x# m2 A4 n. a4 _
. e# h) u* R5 B1 t. K' ^3 a
不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!
! H* S9 p! C [. A' h' Y: \0 W/ g' i* ~) ?! ]0 e; C
' X5 A" R+ b2 K+ s
3 i+ i2 x# ], d6 c% ~
|
zan
|