- 在线时间
- 482 小时
- 最后登录
- 2026-9-11
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7949 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2976
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1183
- 主题
- 1198
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。
# W. D* z( R- Z( H2 g5 n5 I$ r% |3 ?6 E
## 1. Dijkstra 算法
1 r3 ?) Q S& O7 \4 f0 }& I; Z! Z# p6 |! Q. B
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。) X+ Q7 d; A* ~
0 L: u, a+ K5 q M" I2 L### 原理步骤
4 W% X* |3 _$ I- s, \6 z
: J# T: s$ W, R H3 o1. **初始化**:
) r! O Y) ?& B6 E% Q2 T/ P2 ?9 D - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。
4 s p0 S* e/ y2 e$ I3 E% u - 初始化一个空的优先队列,用于存储待处理的节点。6 E8 A3 g6 M( I
1 z3 d# f. h W: z) u
2. **处理节点**:
( b. ]0 v/ L' l) Y - 从优先队列中选取距离最小的节点作为当前节点。4 A! K" V5 u/ c& u! U* E
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。! H% U* u# {& G5 d! L: e
3 ~$ n0 N& k; e/ N7 B- O5 p/ }
3. **重复处理**:
0 M$ [) |/ T7 u6 m3 J0 I* A1 r - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。2 C; W: }: P4 P# C" T5 c
7 b+ l, }1 M2 h2 n) a3 R### 示例代码
8 [% ^& s$ V+ `2 L j3 f3 _0 ? q: j5 e7 V( T r
```python. P4 ?8 f; V$ B: Y. y: G
import heapq2 W# x5 J* Y; _6 b& f
% g( Z8 W+ U8 k% rdef dijkstra(graph, start):
, S. M m2 T& }, V # 图的表示为字典,键为节点,值为邻接节点及其边权+ l9 u, t0 `5 q8 |& P
queue = []) y" j% N7 C2 }8 @- }, r
distances = {node: float('inf') for node in graph}
1 y$ b+ `1 B. ^/ ?6 z* ~ e distances[start] = 0% U! D; |' E+ L: W7 L
heapq.heappush(queue, (0, start)) # (距离, 节点)
# w6 N3 o7 O5 z8 m9 M, ?+ A7 ?: c- h. w$ F
while queue:! L3 Z5 f. X) D, X
current_distance, current_node = heapq.heappop(queue)
8 b; x+ \6 H3 P7 H; Z' R" ~- ?: m3 I6 n: ?! {
# 只处理当前节点的最短距离6 o) [* M) I$ [! }5 A9 ]
if current_distance > distances[current_node]:! ]0 {1 Y% h6 T X. R, _8 H# A1 g! n
continue
- h6 ]; h, }) Z! P( s5 r! \& s% q+ T
' R# z# _! @& a8 m5 a3 O for neighbor, weight in graph[current_node].items():
3 ^: [7 ?$ b) ~1 o) H8 V distance = current_distance + weight
: m! N' A% ^3 J
9 S6 c) f! B6 m+ G" s% f # 更新邻接节点的距离
/ T( P* Y3 l: f; _ if distance < distances[neighbor]:( L5 x' y1 b; W! n6 \5 ]: F
distances[neighbor] = distance
$ ~4 v- r+ k: C w! K: _- t heapq.heappush(queue, (distance, neighbor))
w) ]1 Q3 Q4 Y, h9 p/ o2 [
/ x4 n: ?1 d1 _2 G; P* O return distances g' K& A' ^1 q4 Z( g
" H, }: g4 ]$ h" e0 u2 z# 示例图9 I7 K$ a9 V) S+ J% N9 @
graph = {
8 R, r0 s( z- @* D 'A': {'B': 1, 'C': 4},
+ c! |1 k& ]7 H; V8 S 'B': {'A': 1, 'C': 2, 'D': 5},' E. M, `* G1 U; y* E6 V9 L
'C': {'A': 4, 'B': 2, 'D': 1},
) j6 }2 x: U0 V% Z' \ 'D': {'B': 5, 'C': 1},
4 |. R5 u" Y/ R" Y g0 [}
5 d2 A, c9 i3 u X- e. {: g1 `& J' M* M' Q }
start_node = 'A'
) Q- u w* q: o. v- M- Rshortest_paths = dijkstra(graph, start_node)$ Z5 Z( d+ Q5 C9 L; o# B! p
print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
( R) f& n: K7 W B( W``` ^$ p8 K' L; q4 F! S' B
; _5 ]' I4 m, |0 d* f## 2. Bellman-Ford 算法$ q9 Q- m ?1 I- G% {: w# Q. Y J0 I
6 J: Y& |+ G0 J' iBellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。6 B |8 _3 y; l0 w7 P& _
5 X7 L5 X' X% Y### 原理步骤
1 q7 h, H# L% k2 t( e/ o% B
" E9 P+ j* g7 l6 a: v! P1. **初始化**:
- |+ P0 _& }! |: W! j) f# t8 t5 n; n - 设置源节点到自己的距离为0,其他节点为无穷大。( Y& r4 U' R, }+ D
3 ]! C! B+ l+ t9 V" b4 m
2. **松弛操作**:
: k, D) Y8 [6 K/ g - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。5 L+ `1 h5 Q9 V% T2 B
$ ]" A. e( _: }9 i7 g
3. **检查负权回路**:
& r4 J: r0 H8 ]5 l8 f- H Z - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。3 w+ @7 F1 q- ? V2 e5 { M- x
( ^' G4 e5 z+ Y# G8 ?3 I3 V6 o
### 示例代码% m/ f- G' h$ A% I
0 l+ R% c0 g9 D2 }1 ^) }% K```python
# q. H) D R1 U9 l1 q ndef bellman_ford(graph, start):
2 k9 m6 |& f4 ?4 q' D9 u # 初始化距离
]: C/ u" p( `) n, C% X distances = {node: float('inf') for node in graph}& q% ^( d; F. q/ ?7 q8 o
distances[start] = 01 s2 {+ |& g/ L* H! E! c2 A/ g" D2 }0 y
9 q! j6 G! i. j" f& b
# 松弛操作
8 G1 E1 b* m* K: e for _ in range(len(graph) - 1):: C9 n- y; v- w. B0 [% `
for u in graph:
! O [1 E6 i: f4 \# q& \+ M1 H for v, weight in graph[u].items():& i' }0 V1 k0 p v7 ~% J
if distances[u] + weight < distances[v]:
+ x+ l; G9 i" h5 u5 e* q# \ distances[v] = distances[u] + weight6 e8 M: j2 ~, \9 T) W# J
% k, d; i6 w7 N6 u* {$ w5 Q7 ?8 q! _ # 检查负权回路3 }0 F6 r4 f! R; w* ]: q6 [8 T
for u in graph:# f8 A+ U3 t, ?! g) b; F7 k1 O: H
for v, weight in graph[u].items():
& H1 H' \0 I5 h; i5 d( H7 C0 e if distances[u] + weight < distances[v]:: s( d8 e% w! _) c# N' \+ O
raise ValueError("Graph contains a negative weight cycle")
. F0 I+ m+ l T' G/ V) j4 a2 \: F* }- _) m* ^3 r; B
return distances
* R7 S) q8 @0 x( I$ E, z
- q5 n. D$ M) Z6 }' ?0 W# 示例图(带有负权边)
) u' ]" R$ N0 }. ^( c! z; ~graph_with_negative_weight = {3 x6 y+ D! R8 R6 Q% }% c/ d: ?
'A': {'B': 1, 'C': 4},& X3 D5 V w# S& _% u1 D7 v1 I* e
'B': {'C': -3, 'D': 2},; _0 ?! V S6 W+ B, }
'C': {},- a5 E( L6 T0 U2 w7 ?% f
'D': {'A': -1}" e$ Y3 x! f. V; N) D
}
% j) J+ w7 `% x f/ y/ q3 J+ P0 m0 O( Z4 }+ u0 p* W) B1 K
start_node = 'A'
/ S: P6 _/ R6 Nshortest_paths = bellman_ford(graph_with_negative_weight, start_node)8 W- |' S- m* }, I
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}
" Z. s0 Y2 E+ i8 D1 ^```, Z! X- K9 |# P- M
. c. H; J( f# N* K. p/ _
## 3. Floyd-Warshall 算法( N3 E0 q6 x# X- T, X
6 a; |' |% y9 v1 V
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。8 p7 \4 k3 l4 o; @2 ^3 L1 i
1 P! z, @9 c$ e3 J2 ?### 原理步骤9 P _* H# B" }# s4 M! X
# G( b: {4 Q; _9 p- P1. **初始化**:( ~' E' G/ F" p9 G
- 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。
+ i2 ^. Q' R1 o* r9 n' H, k+ |; c+ C
2. **更新路径**:4 P9 k4 V5 C: P. M6 \
- 三重循环遍历所有节点,以每个中间节点尝试更新路径。
% B1 h, Q0 @4 U! ^7 K
0 `1 C, r; \: V* x6 |5 b3. **输出结果**:
+ F( d- H8 h8 Q0 H7 E- s - 最终得到的矩阵即为每对节点之间的最短路径长度。
0 |! W! S0 _. i# f& ~7 j
# w5 s8 p) V1 U# z0 j% c### 示例代码/ m# g! N+ O6 f- @# E
7 F1 R. r" _% i% ]' |/ W
```python5 ] Q" E, A) |
def floyd_warshall(graph):
% _5 @/ A" V: I # 初始化距离矩阵2 _( B& S0 w9 I! c: b
nodes = list(graph.keys())
& y7 E% ]( g0 A/ I8 \% H, {" y distances = {node: {n: float('inf') for n in nodes} for node in nodes}
# H6 k& i! A- T) B! x* n9 w3 K% T9 @5 x% J/ C+ b
for u in nodes:6 W2 S0 g- C! R8 H, D
distances[u][u] = 0
% F: j8 H. m# t, P/ @0 a for v, weight in graph[u].items():
& d, W2 X8 o4 v distances[u][v] = weight, \& }( k: S1 g* b; O. S: P, U, U
, q* N& f: L& S5 ]- N2 U0 W& M # 更新路径
6 N" y( E( r/ R: o! e for k in nodes:
: c L' l4 y' v6 t for i in nodes:# b$ M/ y9 B. Q2 c
for j in nodes:
: T7 X6 N! F6 G8 o8 f$ f if distances[i][j] > distances[i][k] + distances[k][j]: L D# Z; U7 z. J6 L) V
distances[i][j] = distances[i][k] + distances[k][j]
/ V# M' a7 K2 M$ z! w5 ], {' l0 t3 ~1 X0 L( @- A; b
return distances
+ E* D* v8 y5 u* C- N9 t* f3 k4 x
# 示例图(可以含负权边)' i& y2 E* v$ J4 n0 g
graph_for_floyd = {
, z3 c. g& m- L7 F' @ 'A': {'B': 3, 'C': 8, 'D': -4},
# g. U! B" ^. o1 r$ n 'B': {'C': 1, 'D': 7},/ A9 W4 f+ b, o4 q, [/ x7 s8 K
'C': {'B': 4},
7 F9 `7 d# `, |/ s. P 'D': {'A': 2, 'C': -5}
5 q# O9 W3 q+ S: a Q1 d9 C( Z}8 V0 J& W+ O! e; S$ \
2 w- D( Y) Y+ ^7 g: l7 z! l$ Qshortest_paths_matrix = floyd_warshall(graph_for_floyd)
K. y) n9 D+ @ g) {0 ]for row in shortest_paths_matrix.items():3 v& ^; [3 |& e; P' j
print(row)6 O* r* I, u F; j8 t6 ^9 [! c
```
7 \( _8 J3 b6 p6 w- P2 e; t. F( E
; a8 c: \4 j2 _" b/ |## 总结
' z; v2 L6 s! v3 x: j# l& o9 z0 O
* {. O. U4 X2 n' X- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。% X( V* u7 D/ |2 y* X
- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。
) s* u4 W; C+ Z, d3 i, [- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。7 F. m L' d) q5 ?
2 p0 o; ?* E! \6 }不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!
: v; ?- s1 i( y' H- Q+ o" P) y- t4 \1 n) e! B
% d: @- b7 B8 |6 e6 }7 ~1 @8 Z* Q# q4 v& ~, N7 ?, O0 [/ W2 _
|
zan
|