- 在线时间
- 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 算法。下面将介绍这些算法的基本原理和具体实现。
a; |: ?, l# T& ~
1 _+ B1 F4 x3 i& s. ]$ ~5 G## 1. Dijkstra 算法5 d- H, W% F# K( |
1 y* Z1 W" i: q6 ]- L# I! u0 CDijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
# P# m0 |0 b# Z& L! c+ l
, X" ?9 k& W4 S5 g### 原理步骤0 P. o3 D' Z& k0 J
3 s. v, J- s8 a+ X6 m3 e' ^1. **初始化**:, Y" ]/ o1 h' l9 A
- 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。" g0 ]8 c, i; M
- 初始化一个空的优先队列,用于存储待处理的节点。, |+ J1 w o6 e, O3 `: K
" y0 t8 O$ [) b8 g
2. **处理节点**:9 V4 S$ b, x$ \" n! k% ^, x8 i
- 从优先队列中选取距离最小的节点作为当前节点。
2 `% I7 Y; J! d0 ~; q9 h, W0 p - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
3 \ I* S( D V) v4 F
2 f4 U# T, p, H' O3. **重复处理**:. E0 r6 u/ V6 N
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。
8 D& a8 n6 h8 W( T: s9 ^$ x
% t) M) P2 r8 @. i, \4 e4 o/ J* C! [### 示例代码
0 ~ d6 z' i# q5 E
0 ~+ r' k6 \7 |: S- ~$ G& o$ h6 t* r' u```python9 Y) d" z) u5 a
import heapq ?3 b* R9 P: M% o+ _1 f+ Z. \: l& E
* |& v' b( G; E6 m. ]def dijkstra(graph, start):
/ \ \" H0 Y6 p: i # 图的表示为字典,键为节点,值为邻接节点及其边权
- `, m$ i6 [7 M" O1 p- Y; ?+ q queue = []! V( _9 p" {, l5 {
distances = {node: float('inf') for node in graph}! L! B, I8 {; a i
distances[start] = 0) q1 p# a4 {/ q5 U
heapq.heappush(queue, (0, start)) # (距离, 节点), H$ h! I8 ]% v
0 l) e2 o3 t; Q; k while queue:
) }- i! V/ V+ C8 g Q' ] a a current_distance, current_node = heapq.heappop(queue)
7 v7 B2 Y! u" J/ N6 e; p
9 b' W4 t3 p: D$ O3 U- L" T # 只处理当前节点的最短距离# j6 o) H8 S- C! u4 |
if current_distance > distances[current_node]:
D# o1 ]% ^, p3 O7 U continue
, l( d r& \ W2 p: q0 i4 r4 |" m7 E7 S* T2 ]% [' b
for neighbor, weight in graph[current_node].items():
p) n, _% y7 p- j distance = current_distance + weight1 \1 B h8 P4 F! o
/ G2 ?6 o( ]& l; D5 j; |) O$ F # 更新邻接节点的距离
X0 G6 ?2 l. G- K& r if distance < distances[neighbor]:8 F0 w1 e E! d% A2 m9 G. s" O
distances[neighbor] = distance
& Y* V, S* \9 \5 L1 C5 h heapq.heappush(queue, (distance, neighbor))
( r' y4 E# r( ?- S! v. \* N3 P' N/ _) k+ h
return distances
3 n; T% [' u( F- k% Z* u% V) a# O" z6 j& V2 j
# 示例图! D6 b: f% a2 U" R" A
graph = {
9 o8 U& x* ]4 D2 L9 [ 'A': {'B': 1, 'C': 4},
$ V. L- O, Q$ r6 P0 b) ` 'B': {'A': 1, 'C': 2, 'D': 5},& F( E5 o+ P% P1 Q9 }
'C': {'A': 4, 'B': 2, 'D': 1},
% ]2 [( y7 T2 W2 v1 J 'D': {'B': 5, 'C': 1},
; A; O7 [% N$ F3 z$ V0 Q3 g, m- H' c) n}
' }* d$ e; R$ S" H9 t
! u7 o! `9 S1 d9 i6 q; i4 fstart_node = 'A'
8 K) n6 ?6 C5 ?- k( {- {shortest_paths = dijkstra(graph, start_node). ~9 h3 s' c* ]0 L8 v. G
print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}7 N( z5 e, n2 |6 _6 ?9 v- k% [
```# ]1 u5 X4 u3 ]# V0 p7 ~% R
# ?; B5 N& B/ j2 A
## 2. Bellman-Ford 算法
- T6 o9 _0 y7 U8 z& x/ p5 r' a7 z# F% `' R% @
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。& [$ I6 [# U4 f+ a' T8 Y2 a
' R* i$ M4 N( t9 m, r/ ^9 X
### 原理步骤/ R( R w" Z* L! X; ^) j8 K
9 j( m' A2 m" m1. **初始化**:8 R+ N) { o6 n6 q6 w# c
- 设置源节点到自己的距离为0,其他节点为无穷大。1 S2 n) `" g) _( l% x& D
4 n3 T: T6 \6 T/ |+ `( X/ H% \/ @2. **松弛操作**:, d$ s+ S/ P; R" d- |9 j Q
- 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。
# a0 ?" s4 t* ~( Q
. k( G. l( q' C. L7 g( }3. **检查负权回路**:. ?5 F# k* c1 w+ L
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。
0 W2 d5 W8 N/ e+ v4 |3 u7 W# K" b' a+ z x7 v4 a
### 示例代码7 ?! S# |! X' R7 t1 Y6 U: e
' U w( |1 T i* c) G# [" ]```python% a$ v9 O" ?- f. b X8 F
def bellman_ford(graph, start):
u: F4 y6 r- t6 E # 初始化距离8 A3 j2 d3 | b" \
distances = {node: float('inf') for node in graph}5 o/ e1 h8 v& n
distances[start] = 0
9 v2 @4 x9 j( Z0 ]1 I* q6 C- ~8 k9 `
, ?" b+ h, o5 s/ F4 D, |! x # 松弛操作( ?! C* i4 f. K& X; r9 L# G
for _ in range(len(graph) - 1):
, b) u( o3 ]" Y: Y5 s5 J for u in graph:: ?3 s0 w* Q/ [3 {. u. [) `# q
for v, weight in graph[u].items():' [4 X" p. x9 W
if distances[u] + weight < distances[v]:
5 ~0 b3 O' H$ P0 W. |( w distances[v] = distances[u] + weight1 x0 g: W+ c3 `( U
) L" s# C5 C3 N* w # 检查负权回路
5 e) D0 E. ~: m for u in graph:
4 w9 _$ T9 c* H' ]& B for v, weight in graph[u].items():+ s+ F* K9 f* G( S& V! D/ K+ L
if distances[u] + weight < distances[v]:& _" k1 W; _/ U+ @+ ?$ w5 ]
raise ValueError("Graph contains a negative weight cycle")9 M: [# F1 @! s" R2 K, e
6 e3 I E0 \- c- t3 r: |# H9 ?) D
return distances
( i0 i- O( p& e% C$ C, ] i* f. Y4 I T' m. M, w
# 示例图(带有负权边)8 t# c9 I, Q. m
graph_with_negative_weight = {8 Q9 g6 h2 d+ o4 B
'A': {'B': 1, 'C': 4},
7 p, g% W% Y6 N# u4 m6 ? 'B': {'C': -3, 'D': 2},
/ J. ~ p$ {5 n6 J2 x- g# X" [ 'C': {},
7 }" j; h4 ^& r6 h8 B2 c' y 'D': {'A': -1}2 I7 s( C/ Z$ j! e$ G& A* i. |
}
6 \) {2 z- N% y; G1 ~7 Y3 O+ d4 N: r) |6 B1 p0 E8 ?- H% ]# f
start_node = 'A'
% L Z6 J& c1 m) h# f# ?shortest_paths = bellman_ford(graph_with_negative_weight, start_node)
+ d0 P" O+ j$ z8 ~/ A# v# jprint(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3} `* y% |7 I O! W
```7 j: m. ]$ V7 O
+ H9 w& F# a7 T7 ?## 3. Floyd-Warshall 算法
2 X8 |: i8 r/ n# j1 n2 W1 n
! N) S( V) `3 L% E6 SFloyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
8 W/ h O+ g6 c* a3 h
' V& D7 k- g* K! N, k### 原理步骤
- ?% g( D8 U( `1 P8 \
0 i& P7 D7 F5 C1. **初始化**:
1 [7 k/ y* _. @4 J - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。: e- w% ~& Z$ o2 M
& }, d: X2 L: J* Y+ V" D2. **更新路径**:
) G* u! v c9 E2 b - 三重循环遍历所有节点,以每个中间节点尝试更新路径。2 [( I% ^4 E$ ?+ w
N9 `* M5 a2 H3. **输出结果**:
6 M ^ H2 H4 T9 s5 u6 l - 最终得到的矩阵即为每对节点之间的最短路径长度。/ `3 r5 R$ i. @' X
* e0 n0 e* G2 v4 A5 A3 v K
### 示例代码
; {* M% s8 c4 O: g* S% n! k6 a1 f$ Y' C: R& v
```python" ^, s; L$ N0 J0 e1 v
def floyd_warshall(graph):- h3 X4 c& I+ N& E. Z+ x I7 L
# 初始化距离矩阵
2 l0 z1 y/ |) W' Q0 k* [) e nodes = list(graph.keys())
* o0 T" m5 X6 [8 C$ b distances = {node: {n: float('inf') for n in nodes} for node in nodes}- H1 S# i% w& h: ]8 f* d, ]0 @+ {
7 d) N7 G) M% l8 O! D
for u in nodes:
4 q4 X. u/ e J2 {6 W distances[u][u] = 09 o: G1 y& Q. b7 L m
for v, weight in graph[u].items():9 y4 W4 j G6 ^. Y) K
distances[u][v] = weight
3 F& s+ ?* u; f" f
) X, l! Q! s$ v5 m8 i # 更新路径3 Z2 ^2 M( T7 y3 T ^* T
for k in nodes:
& g/ @& ], e% s1 ?/ p! a for i in nodes:/ G w# A4 M' H
for j in nodes:
/ _( b7 c; G% H. N- [1 ? if distances[i][j] > distances[i][k] + distances[k][j]:
$ u2 ~# M- n. d4 f" P distances[i][j] = distances[i][k] + distances[k][j]% h, D! d% T; W0 j6 l
) V" R6 R6 z/ M+ Z w }
return distances
9 B; s& ?9 g% M8 m0 w3 |
6 @; J# S2 W* e! K# 示例图(可以含负权边)
( X6 d( [: L3 @/ }# Ograph_for_floyd = {2 F4 i v: c w9 G+ x
'A': {'B': 3, 'C': 8, 'D': -4},
! X# L1 n! f0 N/ o) m$ W 'B': {'C': 1, 'D': 7},
1 C/ z! a: L2 D) Z" L5 J* T 'C': {'B': 4},# n" i3 j! M2 K, M8 w6 K
'D': {'A': 2, 'C': -5}3 d2 v' P3 t7 K& ?, s* [
}. n! @/ s2 R! x7 l% s+ `
* s$ q$ @' W/ v2 L9 u: fshortest_paths_matrix = floyd_warshall(graph_for_floyd)
9 j9 K J4 V4 P8 v7 t2 kfor row in shortest_paths_matrix.items():: F1 m- w' k1 j2 X* Y- a* h
print(row)
9 @8 O6 l" S0 h. Z' X3 I9 F7 K, v+ u0 M```
' k" ]* K" n. N) u
0 O: u0 z2 B' h6 j## 总结, ^+ X* e, V; k3 l% F2 ~; Z* C* B2 u
3 o! k( U5 }& C _4 U
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
: o$ K6 x! ?) R- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。
1 `1 a2 d- _% c1 S- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。5 y+ _! ?: z1 c# F9 `! S. J
4 g! m! I! e. e- v6 s9 O
不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!
( i/ r0 U8 O u. p- q; e6 ~; }+ f" J! h( h. H
: T. k/ f3 x( e/ t2 e- {$ v
8 f k2 b6 v8 z
|
zan
|