- 在线时间
- 477 小时
- 最后登录
- 2025-12-17
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7772 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2916
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1169
- 主题
- 1184
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。5 e. l* b* c, I" T' O
6 c8 o b8 [+ B
## 1. Dijkstra 算法+ E$ V; G! k+ ^8 a" ?4 p" X% C
( | ^& Z; k% H, s! b: e& z' j
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
" {0 E9 a1 ?6 q
, b$ o& r9 p9 |7 U' N$ }; |0 V### 原理步骤
; C' {6 c9 G$ y$ l4 d* h
5 E; ?4 H0 U+ r+ a1. **初始化**:
4 \7 u- v3 \, l; B5 k8 r% h6 c - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。
* ^) u) g9 I% }" s - 初始化一个空的优先队列,用于存储待处理的节点。9 D% v# ^8 [: L' f6 L( \7 H
& y) ~. b& L4 m, A( Y, ]) M% g7 C0 x2. **处理节点**:5 ~5 [, p- C/ o3 P. `; ]' K5 s
- 从优先队列中选取距离最小的节点作为当前节点。0 T! ^* s2 Z3 l( f3 j1 A
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
4 j, w. w9 R: o, k' M
& R, G) E- r+ e; F" N3. **重复处理**:3 w$ q/ P* `; s6 H: ]- \$ ], y
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。
# Y, y+ H% \9 ?7 X, U' ]; d1 {- D( |" |4 R* S: I/ ]
### 示例代码( E0 L' v3 X& B' |
) U& q W" r" c- g- r
```python. r& q I* P# p% q2 H
import heapq
/ |7 p8 ?- h( F$ V3 y$ p3 @) O1 U8 c/ H
' H! {, F, R) Q* a. Wdef dijkstra(graph, start):7 E/ H& c* J% |; q* w' {
# 图的表示为字典,键为节点,值为邻接节点及其边权: Z4 Z w. G: Y! Y4 j5 @: @
queue = []
2 ~. g; ^8 M" Q: E7 w distances = {node: float('inf') for node in graph}
4 T1 n3 T* J1 M; K distances[start] = 0
; C3 Y" S" S# @0 i heapq.heappush(queue, (0, start)) # (距离, 节点) X3 }' q/ x" l5 ~0 L
{7 {) j( h+ @) A, X
while queue:- G' z5 H; s. `" Q, T
current_distance, current_node = heapq.heappop(queue)
4 t. J& i& W$ [8 I3 U, R5 c+ v4 p& d* W, x8 C( f) x [
# 只处理当前节点的最短距离
0 n" ]% l3 x$ q/ X p if current_distance > distances[current_node]:0 \% H% o5 f4 e+ D/ q- M6 r
continue( r; b& [5 G- } q. S+ m
% V5 H: G: E3 u+ j4 x for neighbor, weight in graph[current_node].items():8 _* y, a- t2 `6 q+ K
distance = current_distance + weight
* J/ ~1 H' p* u# _, i. d; ?. G5 m) W) E
# 更新邻接节点的距离
1 [. C' n6 \. y+ P! ~4 s0 x0 p2 ^ if distance < distances[neighbor]:% g" D" k3 f1 A, C' q$ X" l% K
distances[neighbor] = distance
0 w, v7 v* m4 e* C* r- y* b/ u heapq.heappush(queue, (distance, neighbor))
K2 l% K1 i4 T2 ^" R/ E! Q
/ Y+ `* o1 d n% d$ I8 W7 Z& g return distances
& j! r( {/ a0 n6 m. ?5 @4 S& m* B8 b
# 示例图
4 B9 y* G: C6 I1 H$ Lgraph = {
9 E6 M- u' j# \2 X2 z9 n 'A': {'B': 1, 'C': 4},$ n* C0 u( g, ^# w9 _ x8 X9 ]3 e
'B': {'A': 1, 'C': 2, 'D': 5},8 C0 G. s" t) j+ B; }& O
'C': {'A': 4, 'B': 2, 'D': 1},/ N: N# N; h) G2 j
'D': {'B': 5, 'C': 1},; |7 ? r7 f3 s5 |; l
}
$ H9 _. k! B- w1 k4 x- _$ a% ?& I/ {, j- a. l
start_node = 'A'
& ]( B ]0 z" n0 I- H8 Tshortest_paths = dijkstra(graph, start_node)
: n( }1 s( Q! T8 y; V0 L9 Jprint(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
$ D) U: O- t4 ~2 ````) b7 {' c/ _7 p3 {4 ~( a; R" m
+ }0 l' [1 v% g5 |- A, |
## 2. Bellman-Ford 算法! E. u+ w& t% V
! ?, w" I8 D" b
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。
( l, S2 X6 p% x1 i; I! z o3 Z$ R3 ]
### 原理步骤& t; f+ g) \6 F
5 ?" T/ }. O. P# ]1. **初始化**:
# f5 Q$ W" _+ S9 G+ X. j$ y - 设置源节点到自己的距离为0,其他节点为无穷大。
- A" k! ?4 H! y' {6 Y0 ?/ B/ e/ k, }; G, K# F* f
2. **松弛操作**:( H' d- J% ~* p+ ^) g
- 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。0 k+ t5 x) {, U% |3 l; m# J
' H7 O6 ^; ~( w- B9 j. b' O! G+ G
3. **检查负权回路**:+ u0 x- y; Q/ B" K+ ^4 `* i
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。
9 }2 l4 f6 L0 \
6 Z' k# K# S/ g- N- e9 c### 示例代码
5 d2 L& V( ^/ K! a3 z! x, r( ~; l
& ^( e) {0 m0 N- n3 |```python9 s7 @/ j) T7 J; b' N
def bellman_ford(graph, start):
( }* V; f( x& P; R # 初始化距离' `9 Q. C* W7 b4 }' S
distances = {node: float('inf') for node in graph}
: Q* [3 [9 h& B# ?1 E' V0 D distances[start] = 0
) p" A+ s0 g5 T5 N& y( q
3 m R% I: r# l. Z6 D, n4 l9 v # 松弛操作& h. L4 ?+ E0 H4 u5 I; w
for _ in range(len(graph) - 1):
& a/ k- ~9 W; C/ [ for u in graph: M# }- ~& Q, ` ]
for v, weight in graph[u].items():- _+ @" i$ O6 B* b6 s$ g6 i) T
if distances[u] + weight < distances[v]:
$ g9 U1 v$ S# o8 ~+ O4 I" ^# [ distances[v] = distances[u] + weight/ ?$ r) @2 H& q" Z5 w& r
! n) u1 R3 D. L0 F( X& X0 \ # 检查负权回路5 u) y( Z$ f4 \+ n& M0 x
for u in graph:% y2 U* x' M5 t- G) h2 `
for v, weight in graph[u].items():
% J% L9 a/ b* w9 G if distances[u] + weight < distances[v]:; @' O& z1 l# M; ?, \1 V7 }' D
raise ValueError("Graph contains a negative weight cycle")! c+ _& T9 U" C5 o- n1 \: E* g
9 f @0 ]+ @* p return distances
. B7 A3 A+ V0 |6 L7 w/ j2 t7 n' b3 N, b% f7 a# V5 q
# 示例图(带有负权边)
5 X! K* P/ f S. h2 O& S/ \. Igraph_with_negative_weight = {0 N4 Z3 n7 g8 w! v
'A': {'B': 1, 'C': 4},
, W R& Y& J$ x1 S0 P4 D 'B': {'C': -3, 'D': 2},
9 C1 R" p9 E* N2 R ^+ [+ U, L 'C': {},* C# ~1 T" A3 z2 _# G# u; G4 ~
'D': {'A': -1}
5 N; Y+ c4 r% r4 D}+ A' U: x- I4 F* _, c' u* O
# H" @) x" V( Z" C5 w+ u, tstart_node = 'A'6 K( z) v0 z5 \" `( \& F- b, Y
shortest_paths = bellman_ford(graph_with_negative_weight, start_node)5 q& g& Q5 f" o7 W+ Z
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}0 T% h& w3 E$ a" h1 _+ s- r' _5 ^
```
1 W; a% X b# K. u# M; ?- f3 K& {' y7 X; t. [9 m' u( v0 x; F2 p
## 3. Floyd-Warshall 算法
' E# i$ M3 g: D/ Q" H4 ]
8 a2 ]2 K) A0 _+ DFloyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。- q* q; @0 b, r4 f9 R% N
! Z' U, y, G, v! {+ E9 }. Z
### 原理步骤. Y2 E$ p' [* B. ?
& k: R5 V u* @) S9 l' ]# u
1. **初始化**:
2 Y2 \! Y9 h8 P: u9 s - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。1 [$ `! n8 D2 ]1 C& s3 ^* _
" T+ `% R: O& |6 c [2. **更新路径**: L0 s: d# Z, ^. R6 d, j0 d2 `
- 三重循环遍历所有节点,以每个中间节点尝试更新路径。2 T5 D* q, c, V/ u
$ G5 A8 @( N; `- X0 \
3. **输出结果**:! Y6 V& ] q; `, |9 K
- 最终得到的矩阵即为每对节点之间的最短路径长度。9 d* i' Y" W- w; [. a9 h6 ?/ P
S9 |5 t1 l: `0 {4 {1 W
### 示例代码
- J$ O( \ O& D' R/ R" c) Z5 J6 r) G: K6 k# s" @. R! S( q9 t
```python
8 i8 p; w6 `: S( F$ F* O/ Odef floyd_warshall(graph):+ X% V6 m h* R
# 初始化距离矩阵
9 N* D" A% K ?) t+ p nodes = list(graph.keys())
& j8 d) e" ^3 l distances = {node: {n: float('inf') for n in nodes} for node in nodes}
* A& l/ Q$ X) a! M
% z, n$ C6 r# n# q8 { for u in nodes:* A& N% u" v" `% G& p
distances[u][u] = 01 U- e2 O2 t& J# E c( J$ m; x
for v, weight in graph[u].items():
0 l0 L6 h1 h) ]( H distances[u][v] = weight
; i. {5 s- D) E
2 S3 x1 v$ d& @7 W, h! G7 Z. g # 更新路径
4 l$ U* V/ `; b7 s) M, d# E/ [" l for k in nodes:$ ~: ^9 X, C; J/ f3 G% u" P) s4 d
for i in nodes:. o8 U, I% H1 E: i
for j in nodes:
+ p; t, I2 i, q! @" n if distances[i][j] > distances[i][k] + distances[k][j]:
! [4 l% y. e8 Z# G5 p; ^7 n& X8 r1 u distances[i][j] = distances[i][k] + distances[k][j]
; Z8 F) L& s) L+ B8 c* v9 Q; I6 n$ v c1 x* V/ {8 [
return distances
, y0 |1 z( j/ J4 L+ b
* v4 @/ a+ v) a/ a; d$ r# 示例图(可以含负权边)
1 F7 z" n1 _% _, d. R! Egraph_for_floyd = {
+ Z' B9 y* o A 'A': {'B': 3, 'C': 8, 'D': -4},
% `! l! L) x1 J. D 'B': {'C': 1, 'D': 7},
, ^: K/ B8 A7 k( F 'C': {'B': 4},4 s, a1 V! h5 m
'D': {'A': 2, 'C': -5}& z4 {8 c8 Z+ n4 C5 T* {
}
3 n% j; r8 [% V& H. d! t) W! [! k/ d1 w! q7 Q2 P) i
shortest_paths_matrix = floyd_warshall(graph_for_floyd)1 N7 m$ m% K% o: Y' V. h
for row in shortest_paths_matrix.items():
/ [7 c+ f5 ^( m( M/ a9 c print(row)4 g) h3 S' y9 k0 `# H# Z
```+ b ~: o; O9 W' W9 {
7 D; D! ]0 w' i4 t9 g1 d## 总结$ K6 f/ A& E M! T+ r9 E" o9 m, A
+ O B4 c+ E) K4 J
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。6 w7 Y" S8 Y" b9 y/ T
- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。, [% D7 \. }6 E( w5 u
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。3 V( T" h6 L: o8 b; s& G0 s
+ H6 q' O; o L- D0 _/ [; v' i, D不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!4 D$ e% K6 d0 b1 a
/ C* j O' j$ z9 y, [7 f3 j6 e* p
9 j# q. r5 q1 H
?! R9 N, F& |7 _ |
zan
|