- 在线时间
- 471 小时
- 最后登录
- 2025-8-11
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7621 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2866
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1160
- 主题
- 1175
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。) ^* G! }( I( F! P N
) U w9 K% }3 r! {3 g$ D8 O0 M" e% p4 v9 q## 1. Dijkstra 算法5 C& G5 g- s0 H; F
9 a$ P& x5 \+ H0 E" {. M$ LDijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
; i7 w, E+ b& b. M7 {# y, V/ z1 f1 d+ H u d1 V/ a, s! `4 p
### 原理步骤
9 s2 ^* ^( E+ o2 S! ^) D( j- S
. d* M. r7 ?9 W- w1. **初始化**:: @4 g" j. l3 o+ ]% z4 C, I
- 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。
* h+ C; O6 C6 `7 ], Y/ q - 初始化一个空的优先队列,用于存储待处理的节点。
8 m, B# O6 j: ]0 W d: a# |1 O/ P/ c1 W w5 }/ \
2. **处理节点**:( ]2 k; }- a5 G" N5 w. h
- 从优先队列中选取距离最小的节点作为当前节点。6 D. f4 W5 u8 w; b* q/ j0 d
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
* E/ _9 u2 J2 C/ b% S+ N
" t" F$ \. Z0 w2 u3. **重复处理**:+ G# k( \7 d: b, J3 ?; g' k8 l- a
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。
* B/ h3 u( a' w6 }6 S1 k" u8 r& ?( ~4 v$ S- y
### 示例代码' q- @1 z& l8 k% I H
, r- p# D) m/ O! w/ t3 t, |# G0 p```python
! y4 F6 _) T1 r; E) i* Uimport heapq
6 c9 j% x' A) a$ B; W
1 d0 Z0 o6 P4 P- jdef dijkstra(graph, start):
! d& A+ `0 G$ R2 N' c4 j' O # 图的表示为字典,键为节点,值为邻接节点及其边权
" M g7 I4 _: b1 {7 c" w0 F queue = []" Q4 a9 a7 w% y+ d, d
distances = {node: float('inf') for node in graph}, N' B; P8 q; O
distances[start] = 0
$ o: p7 ~% L! g2 j4 y+ m" N heapq.heappush(queue, (0, start)) # (距离, 节点)
- w% k9 c0 Q9 c* w* w
8 C1 A0 e. I7 P# u, X while queue:- Y+ B" W, P, J6 k
current_distance, current_node = heapq.heappop(queue)6 V- A- i( B' Q4 r4 i: N
9 Z! d. M4 D7 @9 D9 k3 a+ V # 只处理当前节点的最短距离
) E2 d( J% v9 T$ d) m( m# ^3 o/ | if current_distance > distances[current_node]:. ~0 ?2 K! ~4 `1 d# C3 L8 t
continue$ r8 D5 F' T/ \
2 [- l' j5 d1 _9 B' [2 y1 N
for neighbor, weight in graph[current_node].items():
: {& b4 V& ~, g distance = current_distance + weight! ^& W7 ^" _. V
$ d, x. X4 c. G& E2 p6 _
# 更新邻接节点的距离" E2 y: S* r2 r, n6 e
if distance < distances[neighbor]:* Y; h( Y* s. T, G
distances[neighbor] = distance
. o7 p& l/ j- {/ {! X% q9 K heapq.heappush(queue, (distance, neighbor))
# Q y* R% l' s" l& j) g9 C2 ]/ C) o0 c% a
return distances0 T; y! G3 |& s
8 p) |0 c2 C+ R, i% m* z% i9 S, z
# 示例图
4 l* } B" D- l0 s, Hgraph = {% z1 u4 V1 r/ ~; s
'A': {'B': 1, 'C': 4},5 x* P+ @" m) B" u
'B': {'A': 1, 'C': 2, 'D': 5},
$ x! |3 i2 _2 a: Q0 l6 H 'C': {'A': 4, 'B': 2, 'D': 1},
' X& R& C9 I2 |8 F- k 'D': {'B': 5, 'C': 1},
9 J9 d; j; r$ E) s}: h- I8 Z7 R$ B1 b$ v
+ `* D$ L. G/ C; k3 h& r8 ]3 a
start_node = 'A'
9 C6 s) y- R' N0 I9 Eshortest_paths = dijkstra(graph, start_node)
R$ L9 `+ i! I5 ~print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}% s, ^9 a" k) X9 X
```0 K- k4 X0 Y. ~1 R4 Z
2 _# @& E; \& Y& _# |9 J" c( O+ @
## 2. Bellman-Ford 算法
8 i- i3 p7 A( y6 K. D. e, E q; {6 l0 `- ?0 x6 |: O# V
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。- f, a# P; G) m% H1 T, O" w
% K! O+ A6 Q7 p
### 原理步骤
$ W$ ?/ z; ^% r' X8 p4 D9 g
2 v3 X. J( J+ Y3 d1. **初始化**:" }/ D2 ]; w! j" n1 G
- 设置源节点到自己的距离为0,其他节点为无穷大。 N6 [4 D2 c( G/ |% p y" K d
- z1 o8 U2 ?; ^' ?
2. **松弛操作**:* G6 {3 G" ~4 L* ]' l7 @4 Y% f
- 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。5 S) g3 S% `5 i# U. X# x
& W' O% I* d7 B3. **检查负权回路**:
4 g; b a$ i% _5 t9 D- l - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。
* @% e9 U$ ^4 V, t4 f( D
7 e% I# p% l c4 U### 示例代码2 |$ ]9 L8 y9 C/ V9 t
) U$ g- J4 `+ `. T' ?
```python
; e' \( F& ]7 e8 t; Q9 e; x/ G, Odef bellman_ford(graph, start):
; E5 ^! I- L0 i; T' k # 初始化距离
$ c( u# f* p7 q% H distances = {node: float('inf') for node in graph}+ Y/ b: Y# R4 i( n! c3 }4 W
distances[start] = 09 @5 L7 M- Y7 j$ ~1 y; F$ |
/ v0 Y% u6 A" D- Q # 松弛操作0 C! j) c& q) L" R3 C, H2 \1 i- k
for _ in range(len(graph) - 1):1 g( Z3 x' Q$ G
for u in graph:
8 F4 N% ?# f' T- _; x5 A* _! k for v, weight in graph[u].items():9 s! \" q0 P7 N( E, Q
if distances[u] + weight < distances[v]:" `- e0 m& R7 u+ ^& m/ w0 K) e0 ~
distances[v] = distances[u] + weight4 \: a/ A- V5 H
9 T5 e2 x' o* Y
# 检查负权回路/ q1 `& Y& ?$ @6 F- P1 {
for u in graph:
! a7 `% v' }5 W% } N for v, weight in graph[u].items():5 S! U7 r! T/ L! i* f9 t& ]: ~
if distances[u] + weight < distances[v]:% ~6 i. v0 o2 s: c, W* j
raise ValueError("Graph contains a negative weight cycle")
4 g) }( d4 q6 U: t7 |$ H b0 C8 n& \8 E9 U; M4 p$ _2 |% }
return distances! g& G7 L( o; J5 C5 M' k1 p1 U
* V, ]9 U* |9 W. D/ r1 X# Y
# 示例图(带有负权边)( n" S; [5 H' C H# m0 y7 |) S8 |5 O
graph_with_negative_weight = {# \5 p1 n% u) ]1 E' d: {# `, ]
'A': {'B': 1, 'C': 4},
1 ~3 n m: ^" p+ r9 D" O 'B': {'C': -3, 'D': 2},+ ?5 g+ ?) M; C) P1 l' m
'C': {},
" W/ b/ g; J" N 'D': {'A': -1}8 z& v" S* O' M1 \/ s
}& n! x0 M8 c1 ]0 _
$ j3 ^7 z; y7 y
start_node = 'A'- Z) r) D+ Q4 v2 ]
shortest_paths = bellman_ford(graph_with_negative_weight, start_node): U+ A5 d2 y, e& p2 Q, s
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}
6 H1 l: s3 a; o8 d( q" z3 |```. F$ K0 b' y- Y0 k
1 K6 `" ~1 ], E W## 3. Floyd-Warshall 算法% W" S3 ]7 y. G" ?) z7 a. n& l
! s$ y3 J1 \- l+ wFloyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。# @4 a$ A! m* G8 x6 R4 Y
+ _- A( q$ |0 t+ Z: ^' E
### 原理步骤4 I$ ^" W% f. E6 c/ M& `
1 O0 \2 I1 U( I
1. **初始化**:
9 d+ Z2 f6 d7 o% m - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。% |8 ]" Z' r8 ]4 I* I
; Q* N$ N, `# J3 o; r$ E
2. **更新路径**:! \8 G- q5 z9 u, ^
- 三重循环遍历所有节点,以每个中间节点尝试更新路径。
! L: T9 G' k/ Z) |2 v" d7 O* a* u/ n9 F
3. **输出结果**:
) K( V1 i' K' A- L2 L" H - 最终得到的矩阵即为每对节点之间的最短路径长度。
9 s: c, }# p7 x0 e& X3 C
- [# i& t. Q: p; f/ o0 M' f### 示例代码- u. s1 f. p! F" [& ?) [
3 u' @: \8 _9 f; n
```python
$ d5 L8 [, f9 ]# K% Mdef floyd_warshall(graph):
6 Q$ y$ V, H2 c2 j/ R/ D # 初始化距离矩阵
4 k% q- u( T) k* u: q$ o nodes = list(graph.keys())' V8 x+ ~7 I4 `, u% O
distances = {node: {n: float('inf') for n in nodes} for node in nodes}
. Q' @+ M. l" E; O2 o
$ u) P# q) X8 E% N& j2 z for u in nodes:
( q( Z7 C' [- i U+ O6 s. j distances[u][u] = 0
7 m7 @' a$ h- _9 D: I p2 S- Y for v, weight in graph[u].items():
" E+ j9 n3 \- J; i* D2 u distances[u][v] = weight
4 Q3 U6 F; c6 b" S+ P: d
+ {+ {/ J1 r( P4 l( e" B, P # 更新路径; m0 b- |" \) Z9 U2 p5 X: x
for k in nodes:, p7 g$ h' ]$ f/ l w w; h$ l
for i in nodes:1 F& B! W2 _2 \2 c3 Z
for j in nodes:4 r5 c7 s" a: `0 Y
if distances[i][j] > distances[i][k] + distances[k][j]:0 Z/ r( d, `2 D+ V1 R5 a
distances[i][j] = distances[i][k] + distances[k][j]
, l* V9 t5 D; r# s5 T
6 r: i/ C7 G/ y0 s$ K3 O( Q0 o) r( g& s return distances/ [1 G9 l# D! N" m' n
: Y) e2 p' Q2 S
# 示例图(可以含负权边)& V7 C! ~; N9 g1 G
graph_for_floyd = {
/ A& m- U l4 f 'A': {'B': 3, 'C': 8, 'D': -4},
( T- `+ ?: w" A p 'B': {'C': 1, 'D': 7},
2 y" {, E& Q5 l2 u 'C': {'B': 4},2 \( T$ S8 ]0 X% \
'D': {'A': 2, 'C': -5}
8 n$ @2 H7 w, {6 U; R}# Y' E1 n3 y6 p6 c
s/ K8 p1 f. k% ]- ?. o9 {
shortest_paths_matrix = floyd_warshall(graph_for_floyd). R( ]+ l; S4 |
for row in shortest_paths_matrix.items():
; X1 ]0 \( A7 q# [6 m$ M print(row)
+ T" v' P, C! l```) @: X( x( t7 B& q/ J% U0 \ _
# C) e$ u) S9 @, {9 b. _' q- u
## 总结8 A4 l+ X9 W# M- {# g
& ]0 [, \. k* K& P/ Q
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
1 o/ g8 f; z2 A# e: N- F; K- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。3 B6 B9 A4 V7 c' h+ H, \5 L; D* U! D* m
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。
6 o. X( W! j9 H6 {5 f+ R( f5 v9 S# W N
不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!
3 X) ~. {) {4 y U: d/ F: _- m4 q& U2 S3 {3 I) ~7 r$ Z
& k1 o) R/ S* Z. a1 t2 A& m: k
( M. P8 A7 F) f |
zan
|