- 在线时间
- 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 算法。下面将介绍这些算法的基本原理和具体实现。0 a" f3 \/ V1 w4 G; |- p6 v d
4 O9 Q1 M. Y+ W6 P( G9 S# ` `3 }% M
## 1. Dijkstra 算法
; b. s% B# U/ t0 w, V7 [4 k, Q
# e2 C1 e4 u K% H; d8 _! `8 vDijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。" t; [( G( t9 } x" {
7 U. F0 `$ [( _: w### 原理步骤
. ^( l( Y. \% {. v
7 W. X) m( Q0 ?* \' \5 K1. **初始化**:( x+ `- U _& K# M. o% W
- 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。
* T: n; c% B& D - 初始化一个空的优先队列,用于存储待处理的节点。# b6 F. b8 H2 ?% U
; z2 F% [' o3 h1 G& f2 Q
2. **处理节点**:9 g3 w( w# H; t0 L2 X5 h) o
- 从优先队列中选取距离最小的节点作为当前节点。6 n$ `" C; {6 m
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
( `8 j6 k- d4 t5 P/ ?
/ C1 W0 Q2 @4 s& _0 y3. **重复处理**:
6 l* I5 L; K8 [" n4 a. S( h - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。: Y0 H. r8 X' s4 y M$ V: W/ P$ `" c
* N |& q" @3 F) A" u: D### 示例代码. G3 P1 |# B( Z+ A4 L u' O8 A
; `) h0 v9 v0 h. K! ^. a. l```python% R) b: u0 e2 w
import heapq
* U4 g) ^- |+ n5 \9 v0 I/ _5 j6 h/ S
* x" t& m! O6 i) x# c! l! ]2 Kdef dijkstra(graph, start):, i" E2 S+ ^6 L. f1 n r9 b2 |6 g# [
# 图的表示为字典,键为节点,值为邻接节点及其边权
. f" F: I' {: Q8 e queue = []
! M( w0 s$ |! ^- T: {) V distances = {node: float('inf') for node in graph}# I% T7 |9 x" Q/ i- ?% H3 [) D# c
distances[start] = 0
9 z) P: f' _/ @ heapq.heappush(queue, (0, start)) # (距离, 节点). h5 _" h. o" Z1 y) g# K, l$ y- H' c
3 Q6 k: H( w& |. w; T5 Q while queue:
6 g, j4 h$ h& v9 A9 g( } current_distance, current_node = heapq.heappop(queue)
7 d3 |( x3 U" F2 n- d ?7 c6 a2 N3 D7 i4 j
# 只处理当前节点的最短距离
3 I- K7 z) m; @ if current_distance > distances[current_node]:5 e# J( Z$ M" i& p0 s: `/ @
continue
( x6 [8 k( f% L' f1 [6 G$ \6 F# @3 Q# P
for neighbor, weight in graph[current_node].items():, N. i, _5 P4 w- t5 H
distance = current_distance + weight
. W9 ~7 x( a7 d2 u
4 C+ { t9 Y1 h; z # 更新邻接节点的距离' W* \' p4 C% r- N: S, K, y
if distance < distances[neighbor]:/ }5 s% G5 p" x g9 a) _# d
distances[neighbor] = distance
" R4 K' v* \) ?8 Q) | heapq.heappush(queue, (distance, neighbor))% O' ]! M- }0 I' x/ q3 Y3 h
5 X& x4 A( I+ d& Y5 t
return distances( b; F0 P+ M0 S- B9 S L) E
1 f6 R; s) S {# 示例图
# c4 k I* o- w. d8 }graph = {
l# q) i I: ~+ i8 D) A. p 'A': {'B': 1, 'C': 4},
% r) p0 @* [! s, F# d2 y9 Y" k 'B': {'A': 1, 'C': 2, 'D': 5},% J' L! L8 q0 g: K0 U6 ^: x
'C': {'A': 4, 'B': 2, 'D': 1},
' m% M" s9 a9 D% d; C 'D': {'B': 5, 'C': 1},
2 O4 i5 M* E% y" _, @2 h! V% E- [}3 {9 ], L( A8 M' e! ?% f
% w4 k x0 ?8 o# Y( f6 Hstart_node = 'A'
0 b5 c' A; y9 T' C7 O4 wshortest_paths = dijkstra(graph, start_node)
+ A9 i0 N- H e& `# r: c" A, Kprint(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
: r) D& ?" o# O+ J```
+ U2 X& ]" A' q* {% S. A) W7 b: `7 k) i1 v
## 2. Bellman-Ford 算法
+ n1 b" M) ^* `2 }7 L# @! `+ M
. z" \( n# Z! i* I8 nBellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。
! @% b6 Z7 m( k7 ^2 V( y3 n; B1 `) K: A
### 原理步骤
% w/ K$ J9 j, {6 N0 H/ ~8 g: d( {; M `( k6 Q. p( s: I6 F
1. **初始化**:& s' M9 l f% q4 r7 P6 Y2 W
- 设置源节点到自己的距离为0,其他节点为无穷大。7 w7 K, ^! l6 l! o# b
, Q$ X( p+ N/ d, ]. j T% p
2. **松弛操作**:
& l2 s* G) ]$ Z% t - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。: ^! f8 }% |4 F, J: z' t
5 ]6 u+ u5 g8 G* E( Z @/ D' t- i3. **检查负权回路**:* V; O6 ?# L, v) \3 w
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。. M, Z3 p+ O4 r8 R8 E
7 l* t; x- J- w' T' H1 p. G### 示例代码
[& Z |3 j- h0 c H
7 A4 t8 O0 G! K+ f2 I```python
) B4 P F0 N' _def bellman_ford(graph, start):5 Y6 h) Q( Y: } c3 P
# 初始化距离1 {2 d5 }4 b. q7 z5 P( V2 T' |: @
distances = {node: float('inf') for node in graph}. ~' s* T9 f' D3 p( V8 @ s9 o
distances[start] = 0
8 W* w: V# k" I* m- f% w$ `6 |/ N- N8 k( C6 W9 T
# 松弛操作" d5 V k; o+ ~2 N0 W
for _ in range(len(graph) - 1):1 x; F) ?& {0 N/ j! x& z
for u in graph:
( l! T/ O$ m" Z- W( `9 e for v, weight in graph[u].items():
' b8 E! }: c e) b0 j if distances[u] + weight < distances[v]:/ q7 X$ ?, @: W$ Y+ |
distances[v] = distances[u] + weight8 f: D- |, M7 e" p7 e5 U7 Q, G
! j: g! O$ U/ ^& y
# 检查负权回路1 i ^7 T: w+ Q* x! ?
for u in graph:
; V; R5 z% l& y7 ~( W for v, weight in graph[u].items():
* b3 l' n1 l7 v$ ?1 ~8 F if distances[u] + weight < distances[v]:
. U8 W+ n3 l& M+ ^5 x I raise ValueError("Graph contains a negative weight cycle")0 Z' Q: H; L' c% N
% _/ `5 a6 A* A! E/ _3 Y+ q0 u return distances
& E) ~; ^7 M# \, w$ O P- ]5 @/ L9 c+ Y J& S
# 示例图(带有负权边): _) @& {. m# r, ~8 u: `' B
graph_with_negative_weight = {, D4 N& V5 B2 b6 S, M. Z& r
'A': {'B': 1, 'C': 4}," z1 N* L, x9 S) e1 T9 a
'B': {'C': -3, 'D': 2},+ _" @: l+ J# Z$ D/ f. K
'C': {},
9 d% [' A# k% ?* w; u 'D': {'A': -1}
0 A2 |/ k, t# A5 G}
2 m% M( w7 J! w( a& Q1 n) W1 i2 S7 {$ R2 L
start_node = 'A'. I4 j/ I. Y0 P8 }$ @
shortest_paths = bellman_ford(graph_with_negative_weight, start_node)3 |/ P2 K' A! U* @9 E
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}
. N7 n" v d" Y: z```
/ I: w2 j" }3 Y0 O; S1 A& g: `; v0 Y- y% e% \1 {, L& a5 w- ^; V4 `
## 3. Floyd-Warshall 算法( L, N% X9 @- ?' |* _( j7 M |- P
$ V2 k3 d0 j) [2 |, u8 `+ q0 W0 K
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
# g6 W+ @/ E' @& ]3 a- \4 _
( m1 V* @0 {! @### 原理步骤( G' p$ \$ \3 g8 S9 Z9 m
- I# x1 ?/ o4 J: j& ~1. **初始化**:
3 s& l( L F# j7 x# _' Y3 ~ - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。7 T4 v# T0 X0 C0 k% ]3 W
/ G; O# J+ q, S# ]+ {
2. **更新路径**:/ q" ]7 a; j! ]. t7 S( N# n! X
- 三重循环遍历所有节点,以每个中间节点尝试更新路径。
6 p: H0 ?" V& [8 P. g# v* h3 O9 m& K% A/ ~4 e3 W* k! j
3. **输出结果**:
5 p$ D+ J. v( f( b( ~6 o) w' c - 最终得到的矩阵即为每对节点之间的最短路径长度。
3 v, F0 B3 Q, I3 a4 T8 \/ l5 s0 E8 b: x& |; E
### 示例代码
% @0 y% X8 b/ i( z7 q" t* n; U( g: N% C
```python0 j! Q+ J2 W9 U x' c6 r7 a, r6 x
def floyd_warshall(graph):
0 i) y/ o" Q. l' d/ N2 ^ # 初始化距离矩阵
/ L# Q1 V6 E2 J* Z nodes = list(graph.keys())
; C! J$ ^; b+ n- H. O distances = {node: {n: float('inf') for n in nodes} for node in nodes}) x1 g/ K9 I5 x4 L j
6 \2 D8 ~# ?7 M2 g, B for u in nodes:# @; t! i7 e- _' a7 w* V, A
distances[u][u] = 0
4 I3 @ \$ K: c9 N8 P5 n8 D& k' K for v, weight in graph[u].items():
/ _( R" S; m8 X6 g. }8 H distances[u][v] = weight
" Q$ A/ w( W" o' O- f6 p) V3 i0 i% c. d
# 更新路径
# @3 Z$ S( I: P7 N* Q6 v1 s for k in nodes:
5 K' d" w' p1 k for i in nodes:/ D& h- @* K6 Z1 w+ c( V
for j in nodes:2 s7 y }; t2 B
if distances[i][j] > distances[i][k] + distances[k][j]:
# k! C, _) V& ] distances[i][j] = distances[i][k] + distances[k][j]2 G" ~% j* i5 k6 r3 ^( j! R8 N2 s
, u: a' f) T: s5 M% q4 _
return distances
, R8 r9 @/ E; Q/ q2 ?" `) i4 u& Z, d5 M7 ]
# 示例图(可以含负权边)$ V- V) C: h% z6 V
graph_for_floyd = {( U7 g5 D6 q" T a
'A': {'B': 3, 'C': 8, 'D': -4},
3 Z% f% f/ v: b( l5 _+ ^: w 'B': {'C': 1, 'D': 7},
: m ^2 L% N. @ u 'C': {'B': 4},, `) |2 q1 u, E |1 E1 k3 F
'D': {'A': 2, 'C': -5}
4 A6 e8 Q) y5 J. I}
# L/ r# R( `7 Y! W0 w( Y& ^6 V# r$ `
shortest_paths_matrix = floyd_warshall(graph_for_floyd)
) t$ U' B8 M+ e& k: n3 U/ m, U. }( ffor row in shortest_paths_matrix.items():
! @) L7 Z8 c4 o$ c0 W print(row)
/ B* |2 Y! Y' ~3 s``` w7 c1 f6 k% R! X
* J# T# K8 R7 D- V
## 总结4 X7 l! e$ p) j& x( \% a6 g4 Q
5 G* s; v! n( A& {* b
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
& l8 Q' I& t& O3 b- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。
* \& c, l. o3 q; t4 I* }1 P& Z- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。
$ T# T" ?9 [/ Z
; o* k; r( V& n) y6 g7 U不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!
9 y! Y. @* }" j8 g7 Q5 \# q/ _" @& W
5 m9 O! P3 g! c
* M6 Q; X4 r9 {( ^) k |
zan
|