- 在线时间
- 482 小时
- 最后登录
- 2026-9-11
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7943 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2975
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1183
- 主题
- 1198
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。2 N+ u; V, Q1 D
! z# j& g {6 E4 p% m## 1. Dijkstra 算法
% W4 f# Z+ G2 `5 z8 |$ m! u4 z- w Z& U- j: e; m
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。! {3 C3 l! X; {# Q! S: \' q- w
4 R: }& b! o0 S! P: a
### 原理步骤
& a6 `+ N! n) `; G/ W! Y. x5 u( C: ~" g7 G; R: d _
1. **初始化**:
+ r/ M! g1 z; t9 D7 x - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。6 s9 Z% c1 T# p6 X5 w5 B c* x
- 初始化一个空的优先队列,用于存储待处理的节点。% z, O! x y! b9 R
# ]8 H6 K% ^) V0 Z! U6 X/ ]: N
2. **处理节点**:
9 B8 K6 [8 T+ L1 N/ p: |/ D+ z" p - 从优先队列中选取距离最小的节点作为当前节点。( ^$ N/ q! T1 g/ F! x1 ~& N
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。: T9 ]" s" N8 s! f: ~
8 X5 A! B1 n; \# k7 { L& `/ `/ g
3. **重复处理**:- G1 ~# k) ]8 P! [
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。) d& }) T1 n9 H# g
* {: R, X& r/ e! P6 y( _
### 示例代码
, m2 Q* l4 N: H0 J" q
5 |+ s- w' o" u* t8 Y```python0 E! E' r6 j. ~' J' d
import heapq
' E6 N: x5 v1 L- t X
/ [8 I0 ^7 n, Z! f2 }$ s; Ddef dijkstra(graph, start):2 P: q$ B) \( `* c
# 图的表示为字典,键为节点,值为邻接节点及其边权9 F g0 ^" A& n4 p( K
queue = []
/ `$ _( {7 ~2 [, R/ m distances = {node: float('inf') for node in graph}
* Z. I! K0 U' N1 y3 d v distances[start] = 0
- c8 U5 `( b( z9 B heapq.heappush(queue, (0, start)) # (距离, 节点)
8 {! C5 q. Y' ]4 l
/ ]$ E5 g% w1 o- K( d+ a while queue:
! f- _- ]* R( F$ D current_distance, current_node = heapq.heappop(queue)0 M3 S/ t- X+ v ^
5 H; O% Q4 y% l; D) e& q
# 只处理当前节点的最短距离% w G% O; O4 F |7 i
if current_distance > distances[current_node]: T/ x$ a, c) e) ?
continue
" Q3 g7 D+ u7 k- \
9 D6 K3 v5 U( Y8 m3 O for neighbor, weight in graph[current_node].items():: D! i% ^& V1 ~
distance = current_distance + weight
) ~- ?/ @5 C2 P; o4 ^# n; B1 r
- ~! L$ u7 p- F; X4 q9 k # 更新邻接节点的距离4 V3 o# r4 Z( ]
if distance < distances[neighbor]:, B C* F' W# U Z
distances[neighbor] = distance
: x; [+ L4 Q8 z$ M# m; } heapq.heappush(queue, (distance, neighbor))) i+ `( h2 I6 d/ q A
. v7 \7 Z) _; @ return distances. F! m" Z4 N8 p3 i8 o/ Q
2 M4 B8 q6 } P. i/ o+ r) S V$ [/ _
# 示例图9 z! a. U! [& E/ T* D
graph = {
2 X% g7 {" \& c& Y8 d3 k* F 'A': {'B': 1, 'C': 4},8 ?; R; W" H7 G- s
'B': {'A': 1, 'C': 2, 'D': 5},
+ Y* B/ n. s$ @ 'C': {'A': 4, 'B': 2, 'D': 1},& p! G# J) N+ d. w& c
'D': {'B': 5, 'C': 1},
6 C7 e5 g# z6 j8 t( }) q2 ^}
& i1 }. @) F: W
6 S- V, E( G$ m2 G* [7 Xstart_node = 'A'
" q, ?9 z! Z: x" }& Q, u w+ ^shortest_paths = dijkstra(graph, start_node)
$ R6 x9 c3 Q' n. h. G! sprint(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}* G; ]( X; [8 i
```* J, s- u: @( N/ g9 e
h2 _- w* A5 h3 D2 u \: e
## 2. Bellman-Ford 算法3 A& p& z' a3 h, i& y9 r
" I4 B2 d( x! _Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。
( ~. q4 e6 `% h2 t, B( q
% f$ a9 p! q; {0 e' h* v### 原理步骤
3 C5 v6 H6 D) S; d" D
; E+ T/ b9 S' V- H) T1. **初始化**:% S7 x& H# k b! C4 N; L- q8 H$ Z& X1 \
- 设置源节点到自己的距离为0,其他节点为无穷大。
0 P5 v. R6 j3 k1 t' g
, C. R; ?7 S4 x6 Z& i2. **松弛操作**:0 G# m7 u! O9 r- |, w
- 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。1 X# x) g i2 m4 A h- G U
0 i) s$ P3 ?/ r- ^3. **检查负权回路**:
! [7 n8 a( E# K9 E6 } - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。
! R+ ?0 u; { j6 q ?1 O3 j4 c
. H8 ?# k S" q### 示例代码
: v+ y" U9 e/ h2 _. V$ K3 x5 f$ P8 Y7 H0 B& I; I7 ~
```python
" V2 @% p9 t$ s2 @* Zdef bellman_ford(graph, start):
! y: T( J- g9 z5 ]* \/ z( _ # 初始化距离& J& j+ K- b6 u; ^
distances = {node: float('inf') for node in graph}
6 g3 C: |8 e& Y# F; C: @ distances[start] = 0
5 k% I2 i) W# N8 C6 {. T- W: L9 E% ?
# 松弛操作: u- V! C* y/ e6 t
for _ in range(len(graph) - 1):' d- V, J0 s! H0 u6 p8 C
for u in graph:
9 h% J* C7 t6 @/ ?" v% i for v, weight in graph[u].items():' u2 ^! s( S+ j& ]5 J3 U0 m# T
if distances[u] + weight < distances[v]:" N& k4 X+ x9 r" E. J! @
distances[v] = distances[u] + weight7 z# { x/ o' F5 j/ U2 s
# s, ]9 T( Q' K g) p+ ?; `% Z
# 检查负权回路
* v/ j9 G1 h* i4 V. ^ for u in graph:
; O i, g& R* V3 n' f' y2 ^4 n; e for v, weight in graph[u].items():: ?/ N3 j0 ~5 E4 u i$ v$ ^+ U
if distances[u] + weight < distances[v]:
; k! C1 J7 j! } raise ValueError("Graph contains a negative weight cycle")0 W6 q0 D' C- @& r: Y3 R& w
9 a# |4 T/ c9 b+ S
return distances" E# A/ z) ~+ D8 n
: | m/ t: ?" H7 ?& s) I l
# 示例图(带有负权边)
8 a% {7 s) m, w3 w* \3 R: lgraph_with_negative_weight = {
3 N/ `3 M/ V$ @ n2 L 'A': {'B': 1, 'C': 4},8 [5 E/ O Y/ f1 K" v0 b7 k8 y
'B': {'C': -3, 'D': 2},
$ M0 `" h0 \( A3 p/ M4 S 'C': {},/ A: C f/ q! ^# u3 P: C4 R# N0 A
'D': {'A': -1}: R! b- z; f- h# z+ ?
}& l( q) h# x9 I1 V0 x
- f' {2 O" S$ B2 V" l1 zstart_node = 'A', s% F- i0 U7 b; v6 `7 f
shortest_paths = bellman_ford(graph_with_negative_weight, start_node). o2 u& }4 L) q2 k
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}
) o( H+ n* q7 [```' ^. P; x: Z) v
, u! F) `9 [! m! Z! r5 ?
## 3. Floyd-Warshall 算法! v! S8 y, ~+ Q8 t/ r p/ v
+ f" S' b1 q4 E. L0 h/ l, w
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
0 s; v# e$ [7 Y8 E6 \3 d* P/ z) X& r& J% e5 }
### 原理步骤8 k/ [9 | U. J3 k
) D3 J7 w/ w. ]1 |
1. **初始化**:
; L9 [' l/ E* d" J - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。5 _0 S5 t1 D) E, s
3 W* d7 i# ?. P7 b7 `2. **更新路径**:2 J7 O3 l6 s. O( }# f# o
- 三重循环遍历所有节点,以每个中间节点尝试更新路径。
& K7 x$ j6 n2 y+ J! A+ ]* X8 S: d8 i9 s
3. **输出结果**:: p: O3 }$ G* R" t
- 最终得到的矩阵即为每对节点之间的最短路径长度。
4 v9 ]- r/ T1 L U0 l! x" D6 Z: Z' }5 z& r# h
### 示例代码
; R) l7 G x L" E$ z+ v5 k. d
+ Q& g6 A& D3 }( K! j k+ K3 v```python
7 u1 T4 k3 U+ K. a& U7 H. r) Hdef floyd_warshall(graph):
( M( F- S: _. s% z6 S3 R' D1 O # 初始化距离矩阵
) w, x6 P' h7 m/ ]3 \& u8 h nodes = list(graph.keys())3 e- N+ t% f! O( R/ G0 P
distances = {node: {n: float('inf') for n in nodes} for node in nodes}
# a0 x6 P: b/ z0 S. q0 w$ b- }
% q# u* D" f0 U* r/ \/ L' ^, j for u in nodes:4 c6 T: Q* ~5 ^* u
distances[u][u] = 0+ [: b- |9 c; }( p( ` a3 I0 o
for v, weight in graph[u].items():) ~1 ~# p/ }8 w4 v2 R) i
distances[u][v] = weight3 }* r% {# ?% B. ?5 [9 Y) f! D
( f4 E, n, s0 v; ^& g, s4 b# _" k' Y
# 更新路径3 l1 e N1 K; M4 }# J
for k in nodes:
, M4 Y' |+ y3 c( Q for i in nodes:
( @ u7 r6 |0 v$ E, e# T for j in nodes:* p8 V7 T+ @+ K+ _' E/ `
if distances[i][j] > distances[i][k] + distances[k][j]:9 v+ r. I/ I0 Z" ^0 n8 a
distances[i][j] = distances[i][k] + distances[k][j]4 A6 Q7 l- N. R6 _. }* ]$ i) H
$ Y1 ~. K. X: P3 X/ g$ c# B return distances
: R, j& ]! k6 d! d) p, h
0 b! p* m' ^ r6 D+ t# 示例图(可以含负权边)
# E5 s8 h# v4 R+ i2 _/ @. Fgraph_for_floyd = {
# M/ a" Q8 F9 w0 |! R$ M) ]3 C" { 'A': {'B': 3, 'C': 8, 'D': -4},
1 f2 {- h6 M) | 'B': {'C': 1, 'D': 7},3 `' B6 U7 o* m' ^" a
'C': {'B': 4},4 r, f3 b6 Y: {/ L
'D': {'A': 2, 'C': -5} U8 ]7 Q- ~2 v
}
- d0 m* |; n6 y6 w9 T6 s8 \. z' G+ |: X7 y9 m! l7 S( u. W
shortest_paths_matrix = floyd_warshall(graph_for_floyd)8 d P8 L( _/ K1 f( F' T
for row in shortest_paths_matrix.items():( H1 x7 z2 C3 X3 a9 d. f7 _
print(row)
% H$ V7 h; e0 c( ^# s9 u: [+ v```& L5 [6 d' L1 A$ F( u
# K& [+ _7 B& k1 K8 n; O, n7 G
## 总结5 w0 H5 {# S* S' f8 Z4 k, v
4 N) g6 z5 y. F! @( ]6 B& j- c* ?
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
" v4 K) j; j: T2 |! f0 M" F: H0 Z- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。. T0 H/ M2 K- ~/ @' @/ X$ `
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。
, s9 A! N' j6 {6 r0 }3 M& }9 y
, R! T L) d& d' X$ e e$ n不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!/ N1 i1 C: F1 P8 [
* ~& M- s" n9 _, E5 h2 O+ t
8 j% f7 I b9 X& A# t$ `* [( m
! U7 x! e I& a! l6 y- f$ U
|
zan
|