在线时间 479 小时 最后登录 2026-4-17 注册时间 2023-7-11 听众数 4 收听数 0 能力 0 分 体力 7790 点 威望 0 点 阅读权限 255 积分 2923 相册 0 日志 0 记录 0 帖子 1171 主题 1186 精华 0 分享 0 好友 1
该用户从未签到
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。
% C" D$ Z# {6 i8 X$ X 2 @$ S" ?! T9 }4 T1 n% Q
## 1. Dijkstra 算法3 f) f+ b% w! f6 V: q
, ^6 a; t, `( J2 W Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。- V& L( v+ J8 D
; L! x7 D9 a" n$ r
### 原理步骤6 _( v1 ?0 h; R) U# r
, M8 O! M# ]4 }5 y 1. **初始化**:, X- Y9 m2 V& P
- 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。
( q4 G2 [$ T8 [4 _ - 初始化一个空的优先队列,用于存储待处理的节点。+ Y/ `9 K' ^3 P. R. L3 I2 X2 \. A
2 A. `" j4 k8 j9 Z! ] 2. **处理节点**:# X9 k9 S: T' z+ u4 U+ Z! A
- 从优先队列中选取距离最小的节点作为当前节点。
! k7 I0 j( c0 ]/ o7 I m' I - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
" Z P$ o8 F) @* L, ]: ~2 N1 }' V: p' }6 d
# \) c C/ B- v |# O: e* V; N 3. **重复处理**:
# n( Q3 R5 ?2 u+ \# w2 Y; w - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。1 b, g/ W+ J0 H) [3 w9 i; Z
u! V6 N3 |# I" u2 `4 L# Y6 H* V! w
### 示例代码" o2 r9 d- l; w7 P$ V
. g" f7 d# C* H* G3 _# H
```python- k6 |- `- Q% k
import heapq; a; I2 o+ j$ C! p4 W) h
( v1 @: x6 N- m$ L
def dijkstra(graph, start):
$ c& i* m% R2 T # 图的表示为字典,键为节点,值为邻接节点及其边权
) R( L- t9 s* v4 K% ` r queue = []
7 {; ~7 Y& g/ j, Y4 g: ~: y" ~, w# y. s distances = {node: float('inf') for node in graph}
, `3 `' t* r+ R' H& M distances[start] = 05 t! @# y6 Q6 z: b: I( ?) A/ i$ d4 L. ^
heapq.heappush(queue, (0, start)) # (距离, 节点)
0 L x$ _, a, f
# K N/ e! [# P2 I) U8 } while queue:" W5 y. h0 N( R9 X
current_distance, current_node = heapq.heappop(queue)
* r( J. e. C8 P( U
, q( Z$ P& g- q5 X # 只处理当前节点的最短距离
, D/ P3 s4 z9 C+ j- G" Y* |% C if current_distance > distances[current_node]:
+ o) |, {/ w9 h% i% ?. A continue
7 Q1 J2 M' j1 {& _ ! Q& g p% b; u) S+ I
for neighbor, weight in graph[current_node].items():
/ W r; _$ ?7 N* D3 c# Y distance = current_distance + weight4 Q+ Q) Q4 I- a0 ~+ W+ c
4 f6 L) H7 S3 o' e3 k P # 更新邻接节点的距离
* ]% k. ]" S: t4 U% `4 y# q+ Z if distance < distances[neighbor]:
7 |' c8 w6 y% }& r+ k distances[neighbor] = distance
" ~, ^- c/ h6 C Q heapq.heappush(queue, (distance, neighbor))7 ?' C/ L1 L* `7 ^/ [- ]! t9 ~
+ F5 K5 ~: T4 R! O4 r
return distances
" W" }* k0 T2 i" L1 _
' K$ k& t; | P. r" t( L # 示例图
/ z, _6 y+ O( g, b. Z# ^ graph = {, ~8 l+ q3 i& k/ f9 A
'A': {'B': 1, 'C': 4},9 S$ W& M5 i& I2 q$ ?6 A% N r( l
'B': {'A': 1, 'C': 2, 'D': 5},
" x; @ ]" t; h 'C': {'A': 4, 'B': 2, 'D': 1},+ q, H& E# E" }# s. B/ z
'D': {'B': 5, 'C': 1},4 f. O2 A1 _# |+ {+ c- m
}
3 u0 J0 T/ ^" z+ S- {7 u% H8 ?; e
" W8 \7 a$ O j, K start_node = 'A'% R8 b7 ?# l% e V( i7 p/ d
shortest_paths = dijkstra(graph, start_node)4 Y& U w/ j: I) x8 Y$ s' b; w9 O+ {
print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}( }, [4 D$ S+ d% e/ T/ b
```
5 h; w: h1 Y, }: H1 l ; [$ C$ `7 M" T5 R7 d+ ?
## 2. Bellman-Ford 算法- I6 p" L/ {' S' f/ Z& X% G
. c4 t" X w5 v7 f8 z% A Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。# Z6 X# e( r) c0 y4 l# [8 [
+ T( {8 b* ?, C: P# p" b
### 原理步骤
# f0 J2 f( S7 H. F; ?4 K8 S 8 B( U& O8 U$ \
1. **初始化**:4 g2 q) B6 b2 J, {) S9 D
- 设置源节点到自己的距离为0,其他节点为无穷大。2 p+ ]1 d! e' F
5 e5 P5 Q$ ` P' z% \
2. **松弛操作**:
' J/ o, l/ _5 H, v" o* x4 |3 d9 H; I - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。
" v5 z2 F' q* {: H8 [ l
* c+ y0 w4 W# F* h) @5 X, Q) e 3. **检查负权回路**:
& ?5 H h2 u5 n7 g1 x. ~, a - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。 B& p) j# @8 u" K' D- g) S# `! n# y
) L% ~7 ^9 \& K9 {' g% i
### 示例代码) X+ L5 T5 [# u# [/ X
- f! M7 {' b% N, ^7 L. C8 ]" S/ f
```python) r1 w' W6 d; M( w3 d. t
def bellman_ford(graph, start):
0 q% K$ ?3 h! u, C # 初始化距离0 o: G4 C/ d4 Z! W
distances = {node: float('inf') for node in graph}
/ H9 J& F- D% a9 m. k9 B, f9 l' F. | distances[start] = 03 ~, K- `* j! ~
, s; X# z% u5 o# g8 ?3 c
# 松弛操作* [1 o) w5 U3 i0 q9 S* j$ a
for _ in range(len(graph) - 1):
! D. o/ [. T" R7 I% y+ v for u in graph:
, u. p( V0 P- C N8 S9 n for v, weight in graph[u].items():. ~, a2 E. e" N$ k7 E9 l4 o
if distances[u] + weight < distances[v]:' r6 A3 _4 Q2 g8 r. q# M# r
distances[v] = distances[u] + weight; i) S4 ]* C' A, v+ ?+ A
* ] s" F* e* E2 W X, `1 s, M; ?
# 检查负权回路
2 g6 X, I* l+ [9 x ^ for u in graph:' c" d7 V/ i! K" @4 Q
for v, weight in graph[u].items():
! Q7 b0 ~: z2 M+ }) z" ^. W if distances[u] + weight < distances[v]:5 J. P/ O, X( ]% i6 `
raise ValueError("Graph contains a negative weight cycle")8 F1 i! k" R+ Z0 O( U4 |1 {- H" W
( l0 Z% C! f. }1 t1 D* K
return distances
% A" k2 q: {( o& \3 q
( c8 b6 d* x: ~9 N* w) c# N6 Y) V # 示例图(带有负权边)
* n5 U6 T6 }. `$ y2 G5 l graph_with_negative_weight = {
V2 `' a% S! S; d 'A': {'B': 1, 'C': 4},% M0 [' d8 a/ a6 e
'B': {'C': -3, 'D': 2},* w! h/ Y" r# y9 j7 `
'C': {},
' R3 n _* ~" y& I! l 'D': {'A': -1}3 ^7 d4 V/ o6 P2 E; P$ L
}
0 @ _" \' v0 L1 u
7 p" Q9 T( [3 i9 m- s: q0 H+ Z% T start_node = 'A'( n+ e' b" r/ @/ F7 q
shortest_paths = bellman_ford(graph_with_negative_weight, start_node)
0 R; A) R& g+ k% q print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}% A @( Q$ m% c. g2 v
```2 l1 \8 d. I' F" v; i
$ G' y# Q9 g9 A" g ## 3. Floyd-Warshall 算法
3 ?6 v ^4 {1 D# T5 n
9 t8 p, o3 e$ R- {2 l: {2 Y; L7 j- A Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
" I4 Y* V3 f" f* o4 C: R" \- D
8 L4 D5 E+ I$ u/ V n ### 原理步骤% S. k" m( B' Z; f
5 ]4 T$ n; C; v5 x
1. **初始化**:
$ s8 @. g, }. n% \' p. j, v - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。$ h& x4 R8 ~8 _' _6 I. Q
8 Q. y" q9 @8 s1 [+ R( s 2. **更新路径**:0 `3 b+ r# q6 @- H) Q
- 三重循环遍历所有节点,以每个中间节点尝试更新路径。
6 f; H+ Q8 Q$ J' ]9 ^6 f; V x- m # ]' K% d& |( C( l2 @: v/ {
3. **输出结果**:
4 p' M6 D0 N/ q - 最终得到的矩阵即为每对节点之间的最短路径长度。7 h0 t; k. _% }9 @7 q. M4 c( P: v
Y5 o( j& O, X8 ^, R
### 示例代码6 L0 Q9 K/ g5 m4 r
. r# V1 H# z6 M. r$ J( @ ```python5 F- [: c' t3 w6 f! U
def floyd_warshall(graph):
' Y% J0 Y4 V/ t) M$ f # 初始化距离矩阵0 o! ~+ W" ^% q
nodes = list(graph.keys())3 Y9 ^" j7 }0 t4 `0 i( W, G E
distances = {node: {n: float('inf') for n in nodes} for node in nodes}
9 r* y* J; S+ C$ ?* m ?! _4 n
. }/ P& O1 N8 @9 i$ W( j, \: l Z, [ for u in nodes:
7 V: D! a7 B# ?( Z$ ~ distances[u][u] = 0
+ h9 b: p _# k: t( s& K! L for v, weight in graph[u].items():
& k+ O3 U2 J" e. N& E! ]) Z1 f distances[u][v] = weight
" S8 c' G, \$ V . B ]* Z* J$ f1 f( g
# 更新路径" h n0 b0 z0 x% d# {
for k in nodes:
8 G8 C/ }8 a2 A" Y+ z: c M$ |5 G for i in nodes:" H# O6 c' l/ H) F9 z; P% v, i3 w
for j in nodes:! y0 p# s3 c+ U/ a
if distances[i][j] > distances[i][k] + distances[k][j]:
( |, Z6 |8 K$ b! J# |% r distances[i][j] = distances[i][k] + distances[k][j]3 j4 ~! l+ j1 | G% X; Y% ^
0 H/ T7 o8 A$ \& z5 ^9 i- M. j return distances0 B0 J$ o: s2 r7 e0 y+ D& V
6 k3 T# L. C) F' [1 {* N- j/ \7 Z, I4 p # 示例图(可以含负权边)
6 m3 M4 m0 n4 [/ ]6 o" n6 x graph_for_floyd = {
! J9 A; ^, r5 ~1 Q2 a( Z 'A': {'B': 3, 'C': 8, 'D': -4},2 t5 P. n L3 g+ K+ N- G
'B': {'C': 1, 'D': 7},
, v3 O, y2 k+ T5 X; i. E ~) U6 w 'C': {'B': 4},& `! @" l$ |: \7 c5 l
'D': {'A': 2, 'C': -5}$ d8 _% a" C+ D% B
}
) F+ x% V) N6 P: @/ j
3 l8 i0 t. H" B1 r" R shortest_paths_matrix = floyd_warshall(graph_for_floyd)) n: r* u8 }! i$ R9 F( W
for row in shortest_paths_matrix.items():2 C6 w2 X# V# Y B
print(row); R/ l" r9 Y3 W% _1 C5 \7 {0 h
```* k; w, S$ d4 H$ m) j
% J+ V- ~% H P# x ## 总结$ u% d& }! `& t9 z; z) d
@$ H" V# A8 J- B! j: ^) \ - **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
; ~2 z4 w, w3 |' _$ n - **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。
3 \; L# @9 C5 L) O( ^6 k# g - **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。
( T9 i: g/ O; W y4 O2 M
) A7 U' m" P* V- A; ~ 不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!9 U' B# a- t u, ~# C
8 o4 G2 {! k- q" P
# h8 }( N) S2 B, N0 H3 ^9 d0 r ; z, k9 f, a, U$ R ^' Q
zan