- 在线时间
- 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 算法。下面将介绍这些算法的基本原理和具体实现。# ~ C. f) u6 C! }2 S- }, }/ M
3 l& A8 m& r- t& S+ q& t8 Z- ]/ r## 1. Dijkstra 算法0 o- L7 J7 `8 M6 F( |. L
, h4 v, L2 j5 p1 |* Z
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
$ n9 x' w3 M0 x, [3 H+ m, G- R/ _2 a2 {& ^$ |
### 原理步骤
9 @ H6 V; g5 r" P+ v
9 E* ~0 u1 A2 k1. **初始化**:
. D4 u2 o% ~0 Q* ]2 J+ [6 v - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。5 i# [2 |$ A, }; a$ W) x6 g7 {
- 初始化一个空的优先队列,用于存储待处理的节点。
9 K \" \6 \9 D# @" ?3 V+ w' j- p/ T5 p1 r& M
2. **处理节点**:
5 M+ n) I% }- c( G" i4 K: i - 从优先队列中选取距离最小的节点作为当前节点。
. v1 X! B9 y! ^% s7 W - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
/ Z2 m7 `- c( \( q4 b9 y( C3 P7 H% M8 c8 I+ y9 |
3. **重复处理**:
& Y& _# }, P5 X8 N! F - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。& @. o* s, S) o, h+ [* e1 D
6 k3 {7 m2 y- y# ?
### 示例代码
% N0 g* T4 O7 r
9 z1 M8 A8 D% ?7 A G: ?```python
) S& P* ~: j: wimport heapq: @. m3 m: T7 f8 u; ]
9 T5 D- E- N/ V
def dijkstra(graph, start):% O" q, m ~; i" W1 p& {
# 图的表示为字典,键为节点,值为邻接节点及其边权
! d9 W% K/ h' f, e queue = []
: E# D9 g# K5 w, b2 Q distances = {node: float('inf') for node in graph}/ J" W( [$ I+ G* M4 Z. }$ F! p
distances[start] = 0
+ P) ?9 Q- ?0 Z. ]4 U heapq.heappush(queue, (0, start)) # (距离, 节点)/ _: Y$ d( n; ]& i. I1 l" E2 _
" _$ u; ]0 J8 j, C while queue:1 U( u: X1 F2 z! n; p
current_distance, current_node = heapq.heappop(queue)
% ~% P/ u3 G0 A/ F% V0 u$ @6 `" j8 h8 `- `! `
# 只处理当前节点的最短距离
) q/ b K7 w! D& U7 p if current_distance > distances[current_node]:
# Y$ U: P2 X, x0 \6 z continue. @ X, O, E1 l; i/ S4 t" ^
0 O9 {1 r X/ w
for neighbor, weight in graph[current_node].items():% ?3 k. [+ Z) Q; J8 ?
distance = current_distance + weight! g* J4 Y: Q# ~/ e: _% L, Y/ X
% S/ @6 V: M, I% O( V- X6 ? # 更新邻接节点的距离
( C1 ~9 w( R. x" Q7 p) y( ]2 c6 Z if distance < distances[neighbor]:' l) Y8 X- i1 c) V
distances[neighbor] = distance% r: Y0 w- E1 G: D6 Q2 d1 o
heapq.heappush(queue, (distance, neighbor))5 r1 j( P9 O3 U2 R3 f) R- `
2 P: S4 U+ l+ a return distances) Y4 ~6 Q/ K4 A" O% b3 Q
0 j0 `& G+ m, W! p
# 示例图. K; ~0 H$ Y6 y k A4 S
graph = {+ l) ^0 \) _' I; t; C
'A': {'B': 1, 'C': 4},( U* ~$ I% n* n# V
'B': {'A': 1, 'C': 2, 'D': 5},6 d6 n: V3 |5 z( g1 g
'C': {'A': 4, 'B': 2, 'D': 1},
. f3 z' W% g6 A6 Y 'D': {'B': 5, 'C': 1},& n# `" ^4 |9 V* Z
}: v+ Z0 o/ R' J4 L9 m7 a/ C
: a$ d. U/ g6 Q$ }. Xstart_node = 'A'
/ k( ~, o: j5 d9 q2 _- Zshortest_paths = dijkstra(graph, start_node)) X& J& _" v7 D2 X$ h8 B
print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
0 a7 r+ q; p1 j, _+ e \4 J5 J; F```2 X4 T5 h# L- z% T7 G" X
% u A7 a! \, S4 I: L## 2. Bellman-Ford 算法& {6 N# E. K. L* W; z, H: f
+ [% h; X9 [/ f, Y( ]6 t. X
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。# |2 D% h# k) p5 K/ Q( c5 [- V
# G$ F& n' b: x### 原理步骤
7 a1 E% i0 U1 c- `- F
9 X* d0 u6 ^4 T3 _8 h1. **初始化**:
! E! E" {4 t+ e4 m6 d - 设置源节点到自己的距离为0,其他节点为无穷大。
! ?# j# N1 I0 o- J0 s
# k R- i1 c/ T* I* Z2. **松弛操作**:' a- r, A# a4 t' j$ V
- 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。
4 G1 G* J! [5 W$ C S0 [' }' C; X+ W0 S% ~: b6 i; I
3. **检查负权回路**:
% A. h8 v4 \; w. T/ q4 L9 v - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。7 s0 l. z: i1 u5 P* R2 u1 m- ]
, N C9 F8 A& }7 l2 f### 示例代码8 U; z }: o' C! I4 l( K
& q5 `; r. y0 e, X: s0 Y```python
* x E* H) R5 j: \3 ldef bellman_ford(graph, start):1 E% [; n |# w$ e1 h' s
# 初始化距离
8 c6 Y( H* {' q$ q$ Y: a1 M. Q distances = {node: float('inf') for node in graph}
v& o- Y; {) Q distances[start] = 0, A5 y# a6 |/ m, L; {3 ]
' b8 v# \, E* N7 B% n. v
# 松弛操作7 O9 C/ T# ^3 b/ m+ u* Y; W
for _ in range(len(graph) - 1):* F9 _6 k% A! g0 n Z
for u in graph:. \: {" _* N3 _5 M; o9 N" ]
for v, weight in graph[u].items():/ l' _/ p X9 U l; _: l9 u
if distances[u] + weight < distances[v]:+ Y; g! `9 O8 ]8 p
distances[v] = distances[u] + weight& j _, y. ] Q% a# {
+ B. r% L2 n. K1 L& |4 d; G8 z, M # 检查负权回路! `" x* b+ l# I6 n+ O
for u in graph:
8 j3 D' X2 Z- N1 w! y for v, weight in graph[u].items():1 [ `, t% B) W0 o, u
if distances[u] + weight < distances[v]:
- B& c. R) K# k: ^6 s raise ValueError("Graph contains a negative weight cycle")
& I z% S" S0 L/ _! c! p
' R" a ?/ {% @ return distances
: d8 h/ J( O0 a) y" }! x( s, Q4 _
# 示例图(带有负权边)
, h+ H! W* U7 k" hgraph_with_negative_weight = {( D8 F' c$ K7 j8 }7 T/ ^7 J
'A': {'B': 1, 'C': 4},% \+ a+ ~' ?) V2 o
'B': {'C': -3, 'D': 2},
7 G7 G: V) h8 u# N" ^' K 'C': {},) q9 G/ z. ~( u8 Q' G+ i
'D': {'A': -1}% i `2 `& H, L# U, B: g
}
9 V7 _, R8 K) ~6 h5 Z4 k, J6 Y
3 m( Z! S3 T# C! Rstart_node = 'A'
& B: O# [' N9 d* @# N2 x( Qshortest_paths = bellman_ford(graph_with_negative_weight, start_node)
/ O3 }3 q7 V7 c. k k# gprint(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}7 `; N% C0 L5 T
```
( j4 [. ^$ ]* `# A: I/ G% W+ _1 Y
## 3. Floyd-Warshall 算法 {7 i O" D8 T% z/ U% F
: l7 K6 f* x2 E+ K
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
1 H/ b6 Q4 j- l; R; U: @' ^
( V' h& u0 r+ a6 v6 Q, U### 原理步骤
c0 \0 ^) ]: O {# {, H% V
8 F( e3 O, K1 S. n, U$ Q1. **初始化**:4 F* I: C" Y" Q8 p8 S
- 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。
0 X9 e0 E2 M# o* J6 {2 }" I4 g2 K& X/ W. K6 y! t. l! F
2. **更新路径**:+ l h! K$ S8 q( }
- 三重循环遍历所有节点,以每个中间节点尝试更新路径。4 d1 s D4 r$ J3 z6 s
1 g6 l. x. q J) J
3. **输出结果**:
6 q$ m V0 d$ ^8 c5 i' Z - 最终得到的矩阵即为每对节点之间的最短路径长度。6 v t# a2 m/ v. Y( R2 _
6 r6 z3 R' W% `0 T7 d- F### 示例代码- W8 J3 h7 i1 Z* o
$ Y( L9 Q! h. u+ ~6 a# ````python
g5 ~# D# w: ^def floyd_warshall(graph):
- w+ X( r( I5 l& o7 L) j # 初始化距离矩阵
) D2 x' x5 U/ E {8 U: O nodes = list(graph.keys())( n. R3 s! x) J* H9 {% m. Q
distances = {node: {n: float('inf') for n in nodes} for node in nodes}
6 L/ g3 L3 j/ k% U
9 f9 c8 }1 ?) O- m6 j for u in nodes:
. m: ~# l1 {. }1 ?0 m+ k, Z6 A distances[u][u] = 0
- G1 Q( l8 ~2 x/ l; } for v, weight in graph[u].items():$ f) ^6 x3 R2 k! h9 p6 q
distances[u][v] = weight# ]$ {. B0 u1 Q
# G& l. e/ P- G. F1 g5 n' s # 更新路径' {# N+ v' w: B! v: t2 O
for k in nodes:+ |5 b$ r5 W. i! @
for i in nodes:
7 i8 o, h8 n- j# p" j for j in nodes:
' _) R2 h5 J1 r. |. ], ~9 s if distances[i][j] > distances[i][k] + distances[k][j]:
& b+ j. n4 @$ ~' I+ m8 C& L distances[i][j] = distances[i][k] + distances[k][j]
) F6 @7 u; N. `, o, Y" M( I" x! z7 `
return distances
# v' @) e- O T: n2 E0 t) s
: o; I" g7 B3 f) {# 示例图(可以含负权边)
) |: v& S. |& i3 {, p) wgraph_for_floyd = {
5 K4 T! v+ F7 i# o. Z 'A': {'B': 3, 'C': 8, 'D': -4},4 Y& l3 |6 m" Z w5 ~3 M+ a3 ]- ?4 K) @
'B': {'C': 1, 'D': 7},* m- w! U( x: _6 J9 t: M
'C': {'B': 4},
0 l8 i1 U3 z: N1 A' T& ~* h. ] 'D': {'A': 2, 'C': -5}
1 Q' {$ o! u4 \3 n4 g( m& G}
4 {3 I( A$ h2 @' V Z
1 i9 T4 r8 Z1 G+ I4 o4 U$ a; Bshortest_paths_matrix = floyd_warshall(graph_for_floyd)) [8 C u& G- @
for row in shortest_paths_matrix.items():
a- w7 h" p$ y g print(row)& h W7 X, O3 u/ P8 k
```% d" [ B7 m- R4 r
+ T; I" ?' M3 Z9 D! O1 o0 _
## 总结* e; o- T# a( ]) T
1 J8 S; O$ I( h' s* T
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。1 m$ l- @; w+ ]7 c. a1 A
- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。* T, ^" _8 X2 t" O* h3 Z i6 n
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。
: S2 Q2 M3 [: r- O8 P
! M1 G: m3 ]2 z; [. r不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!
% m) H+ B- l+ ?# \' Z8 V9 U# P0 C
) n2 ~, ~; y4 u! C' A. ]$ i
( F0 c" s+ A# u3 f8 e- e1 c8 W$ e( C7 \: s; ~
|
zan
|