- 在线时间
- 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 算法。下面将介绍这些算法的基本原理和具体实现。) ~5 C% `1 T$ G @* e5 i3 O; [
; u! V+ _; a6 g% U* n7 e## 1. Dijkstra 算法
, I/ x2 P H! S2 i; @/ O; ^* o, W L1 F1 S" p
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。" c: n* Y: R- q0 p/ x& s. ]
5 _2 `5 [& a; H, K. X9 d6 Q### 原理步骤
* k# h) b# b' V. f
% ^, C5 H3 w$ ^# K1. **初始化**:! a- q6 m/ d! T$ X k$ {* b
- 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。* d) n9 P+ b# M! u U/ ]. _4 V) R
- 初始化一个空的优先队列,用于存储待处理的节点。0 P6 {3 _" W: `; p. C( c2 Q
! H0 ~8 }3 }/ |0 s4 o$ p7 Z2. **处理节点**:
|/ k3 L' \. {0 \ - 从优先队列中选取距离最小的节点作为当前节点。
4 J7 p1 _# q6 U# D - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。1 V% h; t4 x: t" d+ N$ G5 ~& [- k
# G' `' D* u$ r6 T* j* n. V$ f
3. **重复处理**:
|; x4 n; {$ M1 b5 U" J m - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。
6 H* b% L& D, X: E$ l% q
$ b8 D+ H' `3 A### 示例代码
- N! }' u: c! n3 H. M4 D0 P+ l7 y* g/ o7 L: c! a3 l6 y
```python# ?1 t6 O4 u( \
import heapq" L7 N0 v3 }: D
- r \; H( Z2 Q$ qdef dijkstra(graph, start):: Y& u P* W& `8 O+ Q. W# B/ z- S
# 图的表示为字典,键为节点,值为邻接节点及其边权9 S K6 Z8 e( h2 ?7 u
queue = []
+ c. D* ^3 c/ ]2 A& F distances = {node: float('inf') for node in graph}
7 e1 i+ q) @4 Y1 P8 P3 U distances[start] = 01 }+ `) l- _7 G/ X, ?- r% T
heapq.heappush(queue, (0, start)) # (距离, 节点)
, B0 Z2 C5 U( |2 Y7 F4 P5 Q2 s- Z9 I9 }3 H' t2 _ Z
while queue:
5 B [- D" ^( o ~ current_distance, current_node = heapq.heappop(queue)
1 j5 k% p- B0 T5 ]' k! V2 Q9 y
; g, }- k4 S8 ` # 只处理当前节点的最短距离
/ d& F( b- q3 U if current_distance > distances[current_node]:2 w u* N: N, R% t$ E! }
continue
# R- \% A3 I$ |$ Y# y; x, W$ `1 X
for neighbor, weight in graph[current_node].items():
4 H7 g( ]7 ^2 B z" R distance = current_distance + weight+ S1 y, P3 L* J T9 A6 a( E4 s: u
0 c. T: e; y1 e1 k7 o1 e( l
# 更新邻接节点的距离4 G9 p+ t- W r; U) a& y
if distance < distances[neighbor]:9 a: [! v6 p6 Z! ?' T- w
distances[neighbor] = distance8 u! H3 Q' B7 m
heapq.heappush(queue, (distance, neighbor)); L* O/ o& m, o/ y" k2 y" E( Z# e
: k% ?# h6 o" B) S+ u6 ]/ W
return distances
, Q v. M" E; Y1 ^
! o$ h2 j+ ~ o: M {: K7 M- q# 示例图, _& N0 i P0 f, ?
graph = {6 p6 n* `; O: R# ]5 Y' e! J2 ^2 ~
'A': {'B': 1, 'C': 4}, k% Z; ?7 R* O9 v- ?
'B': {'A': 1, 'C': 2, 'D': 5},
: H$ y1 f" E9 k6 p( ^" \ 'C': {'A': 4, 'B': 2, 'D': 1},
. L+ P7 \3 e& E2 }8 h& j 'D': {'B': 5, 'C': 1},
' U7 S N1 `/ q2 S; g2 s# z}
% T% P- M4 @( C t- U+ v9 L
: v' }( ~, g; Astart_node = 'A'
S3 T) Q/ m; H* q( g X$ bshortest_paths = dijkstra(graph, start_node)
3 p5 V4 k8 O& B4 A6 m* bprint(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
% I& W' P1 @: `6 r* A- y, Q```
; P% W2 K( e( j( M8 Y4 n! ^/ m- U2 s) g
## 2. Bellman-Ford 算法+ O+ R& d( S9 g. {
9 z% C( F( I% s3 G- y, q
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。
+ B* L# F, P8 E+ H" Q# O) C# ?+ N5 j' T' X. c1 `' P" `
### 原理步骤
1 H# b1 w6 U0 H: g, g# j& G: n3 V2 `9 ]# h) z
1. **初始化**:7 k; m3 X( |. P# G) L' _
- 设置源节点到自己的距离为0,其他节点为无穷大。
$ l5 R1 I# g6 s( H7 x5 o2 ?9 I$ e/ V# T2 V4 L7 O
2. **松弛操作**:! a; @2 |/ i8 T
- 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。6 O% M' H9 Q; f* o1 F
, K' @ y5 L" S5 o' Q
3. **检查负权回路**:+ V1 ^4 k/ b% c
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。7 X9 N# B% w4 f( w6 ^4 h. K5 O
. x* y# f* j! v+ q
### 示例代码
( f# |+ i& o$ r4 c" b+ w2 Q$ s( j# I' u4 g7 X7 Y
```python U5 c' ~* h: K) y3 C3 |3 M# r5 N* W9 b
def bellman_ford(graph, start):
8 S$ Z$ H* h: ]2 _, F Y) I # 初始化距离 e5 u; U& S5 u; d. @
distances = {node: float('inf') for node in graph}4 {+ h$ [% d' J. @# Y! g
distances[start] = 0
. J; q. W2 u. [
) Q) d+ |6 \% l, r% H( r! ^' T # 松弛操作
: s: P- w) Y O for _ in range(len(graph) - 1):
' r* `" N$ k6 R+ A( E% H3 T! k for u in graph:( G' ]- @) I* P- u
for v, weight in graph[u].items():- z& `( n0 m! ?7 {, N. H
if distances[u] + weight < distances[v]:
2 A9 a( d2 J3 o% w* x H( J Y distances[v] = distances[u] + weight5 |# ?, D3 Y0 }1 a" B
9 Z3 `/ J. T! b, z # 检查负权回路
4 i( v% e0 w9 R% f4 ~1 Q( Y2 _ for u in graph:
2 ]4 |" V4 U" n: e. d7 i I1 ? for v, weight in graph[u].items():' A% K! |5 _. e; ~9 Z: o6 m1 ]
if distances[u] + weight < distances[v]:) f" M/ y9 W/ U2 @. L8 I5 t
raise ValueError("Graph contains a negative weight cycle")
1 L+ R) N' L" B% p7 q/ S* k& k4 D" G# Y9 T9 {/ ^. N8 V
return distances
, C+ @& A* K& ]& {6 |
; g( v) \4 _2 w2 ?* B( S# 示例图(带有负权边)- _0 q+ K5 w7 y
graph_with_negative_weight = {1 u3 t2 f0 p( Q; f6 X3 ?
'A': {'B': 1, 'C': 4},* \- [5 E3 V6 c0 C( Q3 }" L5 t
'B': {'C': -3, 'D': 2},- t+ C+ p( q5 H* ~$ j0 R
'C': {},7 N5 K9 ]8 f) a- q5 x
'D': {'A': -1}
l( h3 N l! e7 W* x; q}
: Z3 I, A4 \0 |( r2 M! U6 h5 T: L: }3 J$ o0 i+ V
start_node = 'A'
7 ~9 V, h& L8 m7 w5 @; Bshortest_paths = bellman_ford(graph_with_negative_weight, start_node)
% r! c z0 @ e2 \ o/ }print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}0 Q9 g0 a* w/ y) f8 |2 _% q7 L
```! Z) [, A0 q; X$ K5 |6 a
/ R6 E1 r& D/ O8 p, n" S n, b$ p## 3. Floyd-Warshall 算法: l3 Z8 _- G, R
' A# \9 B/ r5 d4 H4 q* X1 a5 j6 wFloyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。. t5 J& x5 s8 G( C
4 L7 P" X! H3 K% ]0 B
### 原理步骤% R0 e" E7 y8 k: S' A3 o6 w0 z
4 Z/ d3 G8 r7 e$ k1 x/ O1. **初始化**:
' e# q1 V7 t1 \3 d2 |% ^ - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。
5 I8 t2 n z6 b& z. S2 j8 x5 V1 I
) K z5 v% q' {2 g2 I0 y, W2. **更新路径**:
* `' a# ]% A9 O1 H+ K0 ~; k2 h - 三重循环遍历所有节点,以每个中间节点尝试更新路径。3 H, W) D% Y0 t; S3 X+ i
8 n6 ]; _) A4 G% g
3. **输出结果**:( A3 W: E. S2 e9 Z3 A
- 最终得到的矩阵即为每对节点之间的最短路径长度。
, F" X& l# z# ^0 ?! U/ F' I, R
. v3 [4 \: H) @" u8 g% P" s' k2 F. B4 G### 示例代码
! O+ }* {% [/ ^# E+ O
! }. [6 v/ j% s+ E! @" ~```python2 w( x0 {( _ A# d" e
def floyd_warshall(graph):
; Q* p' n8 y" {" p # 初始化距离矩阵- Y& l7 [+ c" p# j1 {$ q
nodes = list(graph.keys())
0 J9 e& c( }6 q( Y distances = {node: {n: float('inf') for n in nodes} for node in nodes}; G% l7 { h; p/ X
: j1 W, M/ c3 O2 F7 ?( d for u in nodes:, z) W! X V$ O: l( F4 j+ G7 A
distances[u][u] = 0
5 k l O1 W2 D3 h" u g for v, weight in graph[u].items():
2 L2 U4 r1 Q( n" L0 {# n* Y distances[u][v] = weight5 S& g. B4 c0 J$ @0 {
+ Y& ?+ x& G4 U: R/ X" k
# 更新路径$ ~% ^7 n1 K8 M# Q+ O" _; c" t( O
for k in nodes:
/ e) I4 T. W2 Y0 m6 A for i in nodes:- S z- \: k0 d- K# B
for j in nodes:% J9 P) p4 _. b7 _$ W% A
if distances[i][j] > distances[i][k] + distances[k][j]:
7 a" A" `8 a9 W distances[i][j] = distances[i][k] + distances[k][j]
1 J" B0 N! y7 a5 Z: o/ y' C/ R% p9 X' N, K! v7 D: b
return distances9 ]- ]8 K1 F! a. ] W+ h
3 c$ {6 b8 J# R7 t
# 示例图(可以含负权边): q9 `5 z' W8 y3 N
graph_for_floyd = {4 S+ @1 e: y, x9 |
'A': {'B': 3, 'C': 8, 'D': -4},# O; O/ F9 k \$ J5 ~( Z3 H) M
'B': {'C': 1, 'D': 7},& I8 ?' a% h( b3 m! L8 ]8 a4 i4 S
'C': {'B': 4}," c1 d0 g" V, m5 X7 S
'D': {'A': 2, 'C': -5}
/ r D) \5 P7 \! S {0 S}8 {4 W# U K0 j1 R1 W2 o
9 |7 C: o1 G+ K7 Pshortest_paths_matrix = floyd_warshall(graph_for_floyd), v; _0 w6 l9 ]# H6 H
for row in shortest_paths_matrix.items():$ x4 }; j$ v6 V, ^
print(row)
3 ^! k- d% v, z, c- B```1 \5 v6 X- Q* U3 Y8 J' w, H3 a; Z
: F' }! Q: ?4 ^/ Q
## 总结
% \7 Y. i1 V" a+ t0 ~, v) x* R' R' t) M1 Z
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。* L/ x3 W/ W, Q- Z
- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。% g8 U' x7 Q8 c, F6 {3 k& J9 o- ]% L
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。& {5 q. u3 U; B- @6 w" Y5 r
- [, Z1 ^' ] @5 \! O不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!
- V' J: T8 ?: \: s$ B: k5 B# m2 [6 F
0 U% x8 A0 w" N& I* W
' r7 B" j0 y/ C# [! X9 B0 i |
zan
|