- 在线时间
- 480 小时
- 最后登录
- 2026-6-1
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7823 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2934
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1174
- 主题
- 1189
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。- ]& U* c+ K$ J7 b2 y: U3 V# p
7 _0 V; B& j* t$ u" |& [+ h## 1. Dijkstra 算法
! n, t" o l! _" Y
; C7 @& a6 `$ ~! U- `Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。+ X2 n+ I3 O# G! x: _8 F
/ S$ ~4 @+ M% w$ l$ w" m
### 原理步骤; r p& t4 H# k1 i# j% C
. j) O1 M# h' v& X4 d9 f; L6 l' J1. **初始化**:
`) q: e. a7 i) G `) Y - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。) ]1 m3 z8 c1 _- ?
- 初始化一个空的优先队列,用于存储待处理的节点。4 _' c9 ?% j. Y g( u% b% \
6 ^ ~4 ]: n; ^1 Z c& n0 W7 t
2. **处理节点**:1 v1 T H z# w. U `
- 从优先队列中选取距离最小的节点作为当前节点。! x/ w6 f8 h) k* L3 u( D7 F/ w' w
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。' b, t" J* I8 C
* I) p" V, o* U- P8 U3. **重复处理**: y; [) X9 x6 ]7 f2 |
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。1 R) e& B* P$ z
4 L) z" @; t7 c$ _0 A1 b### 示例代码3 _. h% A/ _9 l5 E9 q4 w) U
. }" K' o2 }* F* A( J1 D
```python
& ~; g$ n' L/ g$ F+ Dimport heapq6 O+ k! p2 k: T3 l2 c
: q! k9 X( [6 [
def dijkstra(graph, start):
, d4 y: y; Y" i0 @ M/ x) y # 图的表示为字典,键为节点,值为邻接节点及其边权. l h4 g' f3 U' ^/ y) p; Z, a0 b
queue = []
h9 ^$ j0 B. t1 v: t distances = {node: float('inf') for node in graph}
: l7 Z, J% T2 P! U8 p* Q- F. X distances[start] = 0/ n C! T) ]( h7 H; Z$ \
heapq.heappush(queue, (0, start)) # (距离, 节点). M; K9 B9 ~* i7 |
0 p& |2 q6 s) n) x5 k6 `
while queue:; R' e& f/ J. [9 E. n
current_distance, current_node = heapq.heappop(queue)
5 t9 M0 v. w8 ~, u4 H' @; b9 |1 I
- [# }7 b+ m3 F3 N # 只处理当前节点的最短距离
& y6 }7 L- F& o; F if current_distance > distances[current_node]:7 ?( j7 E$ S: S& h* l6 r
continue
/ W5 C/ G# C! L3 H0 [4 ?! {0 M8 `! F; O% n* A `
for neighbor, weight in graph[current_node].items():1 y0 T) {) _' W: Z0 p# Q/ G4 T
distance = current_distance + weight
( u" J9 \) z' Y$ ?, w& W1 O9 \- E2 |' a
# 更新邻接节点的距离. ^% Z2 J* Y9 w
if distance < distances[neighbor]:+ [5 k- }3 s7 D8 b8 K
distances[neighbor] = distance, ^- H9 X* [* e0 j1 B& N* H! B* E3 `
heapq.heappush(queue, (distance, neighbor))% ]; w3 R7 O! E& ]. ]4 T
4 Q0 Q p# t& n) G! }6 ~# } return distances
/ P6 v( \3 A) J: U/ c- B( l
% F1 J, q1 z. Z9 a6 q$ n) w9 s# 示例图4 _* P; H$ h3 O. n( v+ H2 w
graph = {
# V$ o# f- |% r) P 'A': {'B': 1, 'C': 4},; U1 Y8 y; \2 A* I" p* V) }
'B': {'A': 1, 'C': 2, 'D': 5},
" _ o# A2 ?9 `8 g/ n 'C': {'A': 4, 'B': 2, 'D': 1},
+ m: E- x& a8 S/ D$ z 'D': {'B': 5, 'C': 1}, j. c" g0 a. u+ i- p
}
; z0 W/ u0 k- O/ @, R7 j% Y: S
8 }4 ~ X- d( v6 {start_node = 'A'
! M# n/ d" ]3 ?4 L u' nshortest_paths = dijkstra(graph, start_node)
1 g- `2 g s, v' F6 H% Y% lprint(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
$ O' G4 w2 i9 g1 ?```
) s9 q$ X2 t4 T& z
: H% C& x( g" w7 T## 2. Bellman-Ford 算法8 C! n3 p( Y" a; t" k# J0 k
, {0 _# F. k( D4 D) H" P" v1 Z% s
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。3 h2 n, e2 @# l6 p3 y
* ~( `! o# j* [1 t% J5 s% Q### 原理步骤
* W `6 u/ r( p% H4 F7 Z: _" L: U0 P5 H9 [6 j% G
1. **初始化**:
2 H* N. W- V+ ]% D; S' x - 设置源节点到自己的距离为0,其他节点为无穷大。
6 [) r1 K% {: Q1 z" O6 V7 u+ M8 X9 N+ ^8 G1 W2 n- x- p2 ^& v" k) g
2. **松弛操作**:
1 Y5 O8 L a7 ]# o- ^ - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。
& I* j% x, U; A- O2 Z% s
; J5 S* X' d5 \3. **检查负权回路**:3 C7 q; J1 r5 u' V: ?
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。4 |7 A+ n! ^7 w) R/ A1 H3 m
! p/ x3 f% d3 |9 d* X* z### 示例代码. x7 B/ }% z, W& K0 W
. M5 i/ l1 D# P4 S. o```python
3 t7 D5 {1 P, J( m) odef bellman_ford(graph, start):5 u0 Q Z1 F! h: @* C+ ~
# 初始化距离: r0 f/ `6 n$ D! L& Q+ m) L
distances = {node: float('inf') for node in graph}, J1 v* K1 J; }4 i$ X& N( j
distances[start] = 0
/ I* X/ F$ N: h
! B K! X# O& V8 m4 S # 松弛操作" H+ v+ i5 d9 D' T, ]
for _ in range(len(graph) - 1):
. P+ a! U9 T8 C1 X for u in graph:
* L+ P: q, L0 P3 d for v, weight in graph[u].items():
5 j# ^6 x( }% Y if distances[u] + weight < distances[v]: W" M! h: N( F6 H$ J, T
distances[v] = distances[u] + weight
& h' K# o4 |8 I1 y+ E9 l7 A8 t: i a+ `4 ?) l: D( u: X
# 检查负权回路
: i, f+ ]$ e h; j3 E3 { for u in graph:$ o/ D, l7 G2 l. i" j
for v, weight in graph[u].items():6 R, c) o0 N$ C P4 O
if distances[u] + weight < distances[v]:
8 c" T6 f3 O7 A' G1 |, d raise ValueError("Graph contains a negative weight cycle"), w, M1 Q% d4 h: J4 d) i
1 d Y. t8 l4 Z7 ~; d9 w% g
return distances' P% G% \, q" |* ]* e/ w
7 Z: V4 _) Z( U" I1 |
# 示例图(带有负权边)
2 ]$ p# s1 d, y& d8 G# |2 Hgraph_with_negative_weight = {
0 b: `. Y8 g2 I$ s' |! ` 'A': {'B': 1, 'C': 4},
3 t' r3 P$ K2 | 'B': {'C': -3, 'D': 2},( a. V* W/ a+ E: h+ x: I& K
'C': {},
! t t, T, e* h1 c 'D': {'A': -1}
+ l. ]1 ]* `! T, l- v}
- `! Z' V4 O4 }
4 u+ i0 S8 \/ A3 ~# z- Mstart_node = 'A'0 T9 P1 v# o6 z
shortest_paths = bellman_ford(graph_with_negative_weight, start_node)0 Q" a! a. r1 s3 Q# c- R
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}
7 J+ Z3 b/ w& N3 T2 {/ o```# C( V6 ?2 M4 n1 M2 e+ J# x
, \' f$ j. T) a/ i# `+ a9 \
## 3. Floyd-Warshall 算法" l$ s4 g/ Y8 R( a$ T" _2 b/ Z
/ X7 }3 T1 s) e w* y
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
+ |: p3 S1 O S9 C2 S+ N* W% t: o* W
: Q5 e% l8 `' w* ?3 p& F### 原理步骤9 T4 s7 L8 n, |0 v6 Y" t, Z
4 F3 B5 b( G& B( o5 D9 s" ]
1. **初始化**:
H ]' Q9 Z, F! u4 A - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。& Y5 W0 u2 }/ a8 P) f
& y% \; B2 e# J9 }) I5 R
2. **更新路径**:
; s9 `7 e$ n. U9 p" R - 三重循环遍历所有节点,以每个中间节点尝试更新路径。
( L: S$ e( B5 I# C) Q* b1 S3 N5 U9 l: G
3. **输出结果**:
. F3 f9 {% p! V x) Z* L - 最终得到的矩阵即为每对节点之间的最短路径长度。, j7 h2 n ?, a, W' f+ L; c$ ]/ r
* \8 U, x% W, s1 @) \" ?
### 示例代码
( S" q/ r( f6 R- }' ~9 ?7 ]" F) D0 B1 |! }" U
```python
/ T+ W% [3 Q2 H t- {) f4 cdef floyd_warshall(graph):( `- A3 C# {) g* A4 L3 o6 @+ W
# 初始化距离矩阵
3 [/ {: @7 f; F! n. B nodes = list(graph.keys())0 G4 }, z; y! f8 D! a
distances = {node: {n: float('inf') for n in nodes} for node in nodes}% y+ g/ _0 j( z
, c2 `2 N3 U P1 m. [- K" F, H for u in nodes:
6 l3 c! A" q8 i/ M distances[u][u] = 0" I" K1 ~" L9 W. l& W% F) j
for v, weight in graph[u].items():
, S& x8 o- x# i# W8 L4 l3 I distances[u][v] = weight+ p. S/ F1 d$ A) B- N
5 S2 @$ V& Y+ ]5 V
# 更新路径
, l* u% ]8 q, O& d- C/ G% Z for k in nodes:/ J$ D+ k! {' v$ ]7 C( v
for i in nodes:3 R k5 J2 q; V/ g8 n) X
for j in nodes:
8 ]8 j3 e( R2 o3 e) _+ ? if distances[i][j] > distances[i][k] + distances[k][j]:
( `* E$ R) P1 T+ o4 E2 i2 L distances[i][j] = distances[i][k] + distances[k][j]7 b* A# ]0 e5 p3 P7 i
7 U, ]( E$ w' T$ T4 h
return distances
* V' a3 o$ {7 f. x' g- Y, H$ u! I( |% C& ]( A
# 示例图(可以含负权边)
, X) j; d9 `- l5 a) p) g4 u& T4 r! m9 e. _graph_for_floyd = { ~' H6 ]1 l9 T/ S# t4 r
'A': {'B': 3, 'C': 8, 'D': -4},
/ h) c3 ]) K+ t* ^9 w! y 'B': {'C': 1, 'D': 7},
3 |0 O5 ^0 h: [+ }0 p+ q. h 'C': {'B': 4},7 a* Q* L. n% g+ h! Z9 h
'D': {'A': 2, 'C': -5}
8 U) C/ U" O, ^) V- f. }( q}
0 i; Y$ X# Z5 l6 V: E' T% L" T" V. C! |. X s# c1 F
shortest_paths_matrix = floyd_warshall(graph_for_floyd)
% ~( Y, H$ r k5 \for row in shortest_paths_matrix.items():6 N8 Q! u% x/ |% C& z. I- U
print(row)
: P s2 n/ o; n- I+ M```- d2 A7 F6 Y+ J1 m' p
1 v( j! m+ b* o- K0 P3 t" z
## 总结9 a- b5 y1 _( I- h$ C
4 z3 S5 Q V, l- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
; D8 A/ E8 K6 k" T* c3 O _3 B7 K- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。
3 T4 s2 M5 A r- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。. E3 L3 H- ?% X2 y* v( a* g
. [( W. T A8 } p9 H9 I6 B% J
不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!2 \& _- j' c1 s
$ J4 H5 ]* Q; I, E% b+ L+ B
* j8 y9 y5 |0 k/ N1 \0 E8 Q# y3 }8 M( `: q) a3 B
|
zan
|