- 在线时间
- 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 算法。下面将介绍这些算法的基本原理和具体实现。
$ ~: w6 m' X$ @2 Q( X% F
+ x2 L& X* t0 l3 v; M# Z$ Z## 1. Dijkstra 算法' k5 ^8 f) G. T$ c$ q8 w$ F
4 b6 |! C! C" x/ n# o) N( S/ u+ @
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
4 z$ R7 k) e; `7 z) d6 ^1 T8 z- ?3 i8 H- W: `* J9 O' Q1 |* Q
### 原理步骤/ _* Y8 o' @5 \
7 ^, }* E1 |1 u9 k7 @4 L" y
1. **初始化**:
% N2 C4 z6 v) Q1 X/ U+ a - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。. ~! c6 F0 i; n0 ~
- 初始化一个空的优先队列,用于存储待处理的节点。
! b1 f2 o8 N/ a% i; D+ O6 E5 D" }& K, z/ x2 R9 U; j) p4 U
2. **处理节点**:
" t; S% `. g- z+ e& p4 w - 从优先队列中选取距离最小的节点作为当前节点。- [) s% e0 O3 n" M8 `8 [
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
! n$ W& l' i, f
, c- C- t! M/ R3. **重复处理**:, V: C' ^0 M0 L0 M
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。& @( h0 Z1 K4 ]) M$ [& ]" q
$ p$ }* R% o% s+ Y4 Y
### 示例代码
4 p. _' |! v+ Y' Z, A
. F4 L$ U$ }# C0 n, T' T O```python
; T! r7 L0 ^7 @' b( Gimport heapq
5 [( c0 A; S! ]' t2 \1 A% f
3 `1 ~9 |7 o5 | `; d. Y6 qdef dijkstra(graph, start):
6 } W4 y& _% h: |- n # 图的表示为字典,键为节点,值为邻接节点及其边权/ }6 i( w' X, A/ n( f
queue = []
) i: o2 D( [5 v8 ~; r; E6 `5 M distances = {node: float('inf') for node in graph}
w1 w) v# R$ s* n+ I; ^: Y distances[start] = 0
: e7 U- Q. |( i2 n f4 Z% H heapq.heappush(queue, (0, start)) # (距离, 节点)
% B" v9 E* n3 {3 z4 p8 w
( x5 m5 o, E6 @1 X/ H# h while queue:
3 ]* }. }$ R8 x% J5 f1 X0 x. D current_distance, current_node = heapq.heappop(queue)
/ `9 r' y- {7 J' V8 B
& y& n- l8 C3 J/ p # 只处理当前节点的最短距离
4 t& S- K |! }2 c% a if current_distance > distances[current_node]:, x; Z, `+ h( _2 W* W' H' w
continue
- S4 @) w1 V _0 X I2 Y% G* W' b4 u; P2 t- ~
for neighbor, weight in graph[current_node].items():
2 s, M2 ?, q6 h2 B5 V distance = current_distance + weight
/ S/ h0 ]( L/ R- b. [
1 I+ K, F* T) d: u0 u/ b. x h # 更新邻接节点的距离
- C# v. i' F( t& B4 w4 O if distance < distances[neighbor]:
% L1 r+ }: U0 ^' i9 W6 P distances[neighbor] = distance! y/ ?: A3 p. z9 B& G/ C1 X
heapq.heappush(queue, (distance, neighbor))
- l+ P ~! q8 |! n/ O9 r, b) l! p7 H+ U5 Q+ O# \6 Z2 z2 e2 a' ?! i% J
return distances/ M0 B0 @& q* O; H7 ?1 Y
) a7 s$ B0 r1 | t+ Q
# 示例图
. E9 g- C& F( h' n) }+ Hgraph = {' P& }# q; w9 u: M8 S2 w# e
'A': {'B': 1, 'C': 4},- v0 B' {9 p1 A% C5 Z
'B': {'A': 1, 'C': 2, 'D': 5},# p9 f7 J. P7 ?& I; Y/ C
'C': {'A': 4, 'B': 2, 'D': 1},; m7 s) d( s6 M/ ~1 s% M
'D': {'B': 5, 'C': 1},: O& q- h; i, x B
}' A% I9 E! Y. R2 V5 ~0 T
: Q5 @' k, Z/ W A; g8 _% c* M# v6 Estart_node = 'A'
3 r% C# T% [! _# w" r$ mshortest_paths = dijkstra(graph, start_node)
J4 `0 {6 ~, |$ v% ^print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
, Z) L9 q6 g. v8 N```
) j0 `0 I) y- [' U
, P) r7 Y" z2 o5 o## 2. Bellman-Ford 算法
/ ]! F( t" C+ n' a) N9 r: a1 C
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。
; b0 Z% b0 ^9 Z) c2 A9 S$ [& T% D/ ?
" n6 \/ e! ]& c: T9 s6 H### 原理步骤
. e$ t/ C9 V, N1 y# A3 R6 w0 `7 C: u/ Z
1. **初始化**:4 C' |( u9 Y( d+ Q* Y& P
- 设置源节点到自己的距离为0,其他节点为无穷大。* h7 [( ^' l8 Z7 g6 M$ A* R( u' V
6 E6 v9 r' L" J
2. **松弛操作**:
% X) }. C1 e8 ]# M. @* _ - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。# G) T G l% ~1 e2 g/ b
/ `9 t0 V: t: Q& B3. **检查负权回路**:
# d+ b- ~) }# \ - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。3 c1 u4 W3 u2 t' Z
% ?4 k6 E1 o: n# f* \### 示例代码1 w; }3 ^* }* Q8 X
' H% ]5 \* L; ~5 ^: e0 j- Z% @
```python
. P' m% K! ^) w) _- e8 xdef bellman_ford(graph, start):3 D0 }: t9 w: O( o
# 初始化距离
& d4 d4 {) N) w: H6 B, K! S distances = {node: float('inf') for node in graph}
3 h+ F) y5 F+ ^3 _4 R$ G distances[start] = 0' O) K/ i9 X2 v4 ]0 @! q! ?+ u% B
5 _2 e; F! h, Q& r$ G0 D9 n& i # 松弛操作' K( x9 Q2 g$ e& E
for _ in range(len(graph) - 1):* z0 l- [2 ?+ W6 T
for u in graph:, c% c6 Y& K4 l! ]
for v, weight in graph[u].items():" q; T) [; ^2 R% K3 w' `( O
if distances[u] + weight < distances[v]:
# ^ x V/ [" {. |3 ~, y9 @ distances[v] = distances[u] + weight5 B, m1 Y/ v4 ^' c
+ i4 b. H7 T6 i
# 检查负权回路
, E+ z! D8 H8 M! U& `0 a' M F3 z for u in graph:
5 a. H" O/ ]; e( Y. f( m for v, weight in graph[u].items():
& W$ X9 V& }- x$ F0 D" |! I if distances[u] + weight < distances[v]:1 L, \) v3 K% E1 K" z$ K8 o) P
raise ValueError("Graph contains a negative weight cycle")
* \; a9 i! y4 V7 r& E; g V
; w' Y& M6 M7 W8 K* y return distances
1 r# \# Z4 ~& P
; y/ U+ w$ r* E/ D, q3 s# 示例图(带有负权边)
# r) @+ h: T( w8 E. hgraph_with_negative_weight = {& V, C+ s6 i: \1 ]$ v
'A': {'B': 1, 'C': 4},& ^' W! ]6 B/ S( x( i* H
'B': {'C': -3, 'D': 2},& n7 ^5 x: i" r5 N7 v9 g) p5 h
'C': {},
5 s; T c3 J) d 'D': {'A': -1}
" }, ]* {8 o) V; I+ M. Z2 k2 E; H}* l$ g: V- T a
i6 k4 S0 e$ h% o/ \start_node = 'A'
8 C) y. y: V7 G P8 j# w, ~* Wshortest_paths = bellman_ford(graph_with_negative_weight, start_node); T( ?# t$ C. O" U
print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}1 x3 z9 g9 W3 `$ P$ n: E! M
```/ G1 p. o8 _. K: v) t. e/ {! L5 o
! h$ E7 O7 k5 k## 3. Floyd-Warshall 算法
, R% ]$ t+ w1 o+ _+ ]8 ?+ s. g) s( A2 {( ^, U: w3 M& x
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
) _$ l9 o# y' {3 N1 N( X% j0 w2 x& \) Q. K& u
### 原理步骤3 N0 d4 ~* e# k F: a7 k0 x
, T" W. r6 U2 Z* p9 W% `: J# w/ l
1. **初始化**:# K* ^" W B1 E7 H$ L% l2 v
- 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。5 X2 n, B1 q8 \( x3 c, }
) ^/ f; U9 G8 @+ y0 ^2 d/ ]2. **更新路径**:
0 ]2 l+ n/ f# \& D: Q: c - 三重循环遍历所有节点,以每个中间节点尝试更新路径。% L7 O) ^9 u7 I! p' `5 ~
/ V- h! I. M) Y. k! b3. **输出结果**:
# `) P! @" ]; v) g. G4 m* r - 最终得到的矩阵即为每对节点之间的最短路径长度。) G0 h& H5 E; r( g$ ]. X. b1 e
3 T( P2 S6 _( F, w1 l2 z
### 示例代码
7 g# C+ n$ }) W& N7 _4 |
- C* ^! y2 P1 n& \```python% p- N9 V3 n6 B$ W" u7 ]5 D2 D
def floyd_warshall(graph):
5 G5 q8 L% [, h$ A # 初始化距离矩阵, m' j1 E8 X8 e( d: z% |
nodes = list(graph.keys())
, b+ W; _) D% ] distances = {node: {n: float('inf') for n in nodes} for node in nodes}, d6 E2 Y1 C+ q
$ Z) Q @ J: |3 `; G3 { for u in nodes:! V6 K) J9 K" S9 ]9 q4 u
distances[u][u] = 0
- ~, } P& S8 |, h for v, weight in graph[u].items():4 ~$ \+ h7 c, |4 l9 b4 P
distances[u][v] = weight
" b# G% M9 Y4 b' U& g2 a b) {2 I$ @7 }3 n, I; w3 I
# 更新路径& { f4 |- f1 B
for k in nodes:
3 J! Q: @0 J- W8 ]" [7 C- M* q for i in nodes: _: a! d9 D1 L3 {7 o& O
for j in nodes:5 u8 Z/ `) y& X' Z# j4 S! A# Y. U
if distances[i][j] > distances[i][k] + distances[k][j]:1 Q1 B: {' Z+ s% x* Y8 Y' S
distances[i][j] = distances[i][k] + distances[k][j]
$ h6 w8 G: L4 n6 {1 ~
& L5 G* B% i& |1 n7 w return distances- @5 }+ W0 ~( o/ K
6 l' @1 t1 ~/ Y- `5 o" d' v
# 示例图(可以含负权边)
L- N3 p( v3 t( r0 K8 C* }graph_for_floyd = {
q: x. T/ q+ B) @& S 'A': {'B': 3, 'C': 8, 'D': -4},) F, w# u. e& t/ x5 z7 ^
'B': {'C': 1, 'D': 7},
- I( E+ M( G s/ a1 A% M 'C': {'B': 4},
7 `; m6 F7 O9 p9 J, A. m8 \0 W 'D': {'A': 2, 'C': -5}
5 w' F4 D/ H* ]& ^6 e}- D+ {( i( w' H
# t: z2 X4 B% @4 H8 z) Eshortest_paths_matrix = floyd_warshall(graph_for_floyd)
$ b7 }+ Y8 A3 R) g7 y. N6 Bfor row in shortest_paths_matrix.items():) n3 \. S x# V: B
print(row)/ o& i6 X9 K2 a8 J, H- _
```
* B w8 R5 V+ r; h
c4 `. D/ m& v9 G( J3 u## 总结
! c# |, R; _; n5 f4 u2 h$ t, {+ V2 N7 t
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
; Q5 `+ z4 Z) R4 _$ } n, [( l- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。
$ Y. X! ~- A( {2 t# P6 m- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。
" a/ E K$ d% n1 e* `' T2 t+ r& S: @
不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!8 y# I- [/ }; U s6 J
! g8 _+ g9 {" s" N- r
f! C& D2 h% Y( r, ^. F2 l; r) B
|
zan
|