- 在线时间
- 482 小时
- 最后登录
- 2026-9-11
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7953 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2978
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1183
- 主题
- 1198
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。/ U- j) u4 t5 @# n1 U! j) l
2 @$ A N# S% q1 E" W
## 1. Dijkstra 算法
3 O' h& g" o- N$ C$ y" \( S$ o8 e" I* }( W. j( P O1 L
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
1 @( B: U& n7 b# x" v& x" ^- o9 a; a
### 原理步骤
! z6 G9 _9 `$ t: S: [2 d/ M5 }/ c& ~6 C S- n7 }
1. **初始化**:
0 W; m: S! t2 s# X) r0 B& X o - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。0 x p. g" P, o8 ^. V3 a
- 初始化一个空的优先队列,用于存储待处理的节点。$ p Z u: W4 _. a3 k/ Y3 y* q
5 h( N8 Q8 T! z
2. **处理节点**:+ A% [( r+ Q) r5 |: _
- 从优先队列中选取距离最小的节点作为当前节点。# H5 E- p9 k" k: Y$ j3 f3 _
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。$ T- u. [! k0 d f3 T5 \
+ [. e+ z9 u! J2 ^3. **重复处理**:
5 \/ `" J% g3 ~ _. R r - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。; g# Y2 w5 K! z" @3 K( B& M
4 n2 R2 `* q: q
### 示例代码
! p3 w( g: D, L3 _$ [7 h; ^) v- a3 m8 V, ^
```python
/ x; C) S3 J' O& Z6 y' zimport heapq
9 k9 r0 {( {( O' k
' V' y" Z* |& o2 @0 F8 _def dijkstra(graph, start):
" T$ `, `' g* m+ C8 `' D+ `/ @) A # 图的表示为字典,键为节点,值为邻接节点及其边权
9 [/ i6 |* f# r7 m& m6 ` queue = []' h: E! f2 U1 S. a! c) s$ T
distances = {node: float('inf') for node in graph}0 D& _+ y; q* f8 w3 ^- z: o4 ^
distances[start] = 05 q( }* ^8 y5 E8 p! m0 u) d
heapq.heappush(queue, (0, start)) # (距离, 节点)# u2 ]+ H+ Q( l0 q2 g6 m9 `/ I# A
. r/ B1 G7 ^ F+ P# u- J3 p
while queue:
+ n5 ^) S* X* ^: i$ Z+ v5 T current_distance, current_node = heapq.heappop(queue)* G: j$ K2 l4 h- n6 c! V8 P4 q( y; @! o' L
2 M0 U2 D- ]7 w' _. j
# 只处理当前节点的最短距离
! J1 |: i+ X' {% @& Q if current_distance > distances[current_node]:+ }; b6 n2 o, ]# D6 ]" n* n
continue) T( m/ J1 N5 j1 m
# O( l5 a* X, U* x# H* ^& J: J1 V3 P9 z
for neighbor, weight in graph[current_node].items():# C6 ^ i, }6 B5 U7 l
distance = current_distance + weight
: _; c. u1 H; P4 j. g9 @2 r* C
" f# `% P9 @- I, j* ~ # 更新邻接节点的距离
* \7 A) V% a6 V2 }+ P if distance < distances[neighbor]:* v& L) X# f/ m% ?
distances[neighbor] = distance
T# q: `! S$ R4 v. N heapq.heappush(queue, (distance, neighbor))
, Z" n8 p) L; p q
: ?1 q1 R- i7 a$ C; P { return distances$ I$ V5 i3 ]; f
9 M, i1 [" o- o+ Q+ k+ P# 示例图
9 W7 p5 s' E+ M7 {# y$ `graph = {2 z f: U% ?% N
'A': {'B': 1, 'C': 4},
1 X3 h) \; @* T1 J8 M8 k 'B': {'A': 1, 'C': 2, 'D': 5},
! p0 j7 z+ ~' w* F3 G; H 'C': {'A': 4, 'B': 2, 'D': 1},+ n7 U' w+ L% M4 d# V! t' |* R
'D': {'B': 5, 'C': 1},
, _2 N. L( T+ G7 j3 d}
* F9 c- j+ M5 j, B
! ~1 |' V# D2 j) cstart_node = 'A'
A# V+ X/ A& g9 q; y! Eshortest_paths = dijkstra(graph, start_node)
8 b6 A- z6 k2 I; F+ Pprint(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}
5 i9 y' X2 j T( v0 A8 ^```
3 k/ [4 h! j/ w q! C% r
! M+ R/ `/ Y h0 R$ a4 v [## 2. Bellman-Ford 算法
/ D# x3 M! C( W z; C( Y2 m: \' b) b% @7 m% J- }0 h$ G6 l& r U7 ?8 I
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。
3 f- a) r6 r' A* J
4 [9 C0 \3 t; X### 原理步骤
+ u# S$ _+ L6 p& Y3 T2 B) v% b: C, ^* }
1. **初始化**:* @3 k* j6 X- e% d
- 设置源节点到自己的距离为0,其他节点为无穷大。' U! C; |4 j4 N* b; u6 W( e* }5 T
, H% E5 w7 {% B6 x) Y9 b: {# F/ b2. **松弛操作**:
. J. x( O, L+ o6 ^, x$ | - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。
" D6 H6 j5 O8 [4 ^9 }' {% K# |, O; ~7 K s$ b
3. **检查负权回路**:) A$ M3 K% \- Z! J0 \
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。( q- {# f+ L4 T) M: [0 e3 ]* \: s
% s2 k$ N/ I/ S2 ]1 i* e9 H
### 示例代码6 n5 B& {' N% d2 L
7 {" q+ N+ C0 g- e+ s
```python
2 _6 a {7 U- @5 k# M; y4 H3 I: idef bellman_ford(graph, start):# {/ Z& J8 H5 u! n
# 初始化距离
( d* t) h F; @- @ distances = {node: float('inf') for node in graph}1 M' @0 c9 g, \3 e5 v- n! t7 }
distances[start] = 0
. ^- j9 H4 F, [$ f( D
5 E- w! V( q! j; x3 t! v7 m # 松弛操作( b2 s$ c: O( a. F
for _ in range(len(graph) - 1):: q. F! h; @6 x
for u in graph:- P I; C7 H9 X1 I$ e' w
for v, weight in graph[u].items():* z6 x* ?" b5 G9 u4 K- i
if distances[u] + weight < distances[v]:
- w: n5 B3 b7 w: E5 V' } distances[v] = distances[u] + weight
- z- g6 n K: Y6 T! K$ l# }+ Q& j; U3 q& v5 ^; {# \, r
# 检查负权回路9 f3 J5 U# y; Q8 V) a2 `) N
for u in graph:
: ]6 U0 a+ P o for v, weight in graph[u].items():
8 i: h' k3 l" R- K" i! J if distances[u] + weight < distances[v]:. w+ b9 n' u2 h+ e2 w- X
raise ValueError("Graph contains a negative weight cycle")) _& |1 b+ z; U; }( y
s. Z( a% J* L* ` return distances
: k: l, i+ G7 {$ G7 f7 ^$ G: b; B: U& e0 K
# 示例图(带有负权边)
; t3 `- w; |9 X0 f% `3 Lgraph_with_negative_weight = {
( G, W w8 k! g0 p/ M 'A': {'B': 1, 'C': 4}," h: U' `; O! H; y- l, G" T/ g
'B': {'C': -3, 'D': 2},
% ~% B# J3 a' E$ u) v 'C': {},+ @- T2 f& s |) Z3 C3 g$ Q
'D': {'A': -1}
3 p0 k8 T+ u% m7 s}: ~0 a: ^; ^) |# Q: Z# n
- I9 Q# [9 V) F! }- ^0 ^* B1 `/ R
start_node = 'A'
: Y8 b0 g' u' p( L5 C, hshortest_paths = bellman_ford(graph_with_negative_weight, start_node)
& t! ~" j7 ]2 ?+ Rprint(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}
% t9 g- v: e- b4 a! q+ @/ d```8 c* A% B3 |$ I' X5 U. [
' g+ b9 q4 G0 _) `" n
## 3. Floyd-Warshall 算法, n3 G9 J1 g3 [; B: m2 x5 I( { p6 R
. ?5 `% w- \) [( z6 q
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。' a' u( x3 z$ J5 s' j( r
5 i1 b' s6 k3 ]3 @& d### 原理步骤
& f4 \$ G% B" F9 |0 W5 ]6 w+ T
4 m9 e; P3 D/ g: |% i1. **初始化**:+ J. `) E* H2 L# H* y6 Y
- 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。2 J( _/ f) `8 u8 J% d& \; U! }# D
3 V ^- d% @% G5 B: @2. **更新路径**:& x4 |9 X9 L0 b1 P
- 三重循环遍历所有节点,以每个中间节点尝试更新路径。* V7 g8 F( H" T2 ^+ z1 ~7 a y
0 ?$ L8 ^2 P' P4 d" _
3. **输出结果**:
( u# ~" x; m7 O* @! Y - 最终得到的矩阵即为每对节点之间的最短路径长度。
' n1 h4 w$ w0 O8 z, v6 L. q; s3 Z% h' ^ a) Z3 y, Z
### 示例代码8 i* F8 C! g6 t! ]9 Z% C
) H/ _* Q3 R4 z; R4 [2 {```python5 L8 C5 ^4 \, h+ {7 S v
def floyd_warshall(graph):, Q9 _) A. @( `/ P& J
# 初始化距离矩阵! ^7 h& q( D% u2 C
nodes = list(graph.keys())( o& E- A9 O7 u4 Z7 n6 H
distances = {node: {n: float('inf') for n in nodes} for node in nodes}7 s. \: e# ^" x; j' ^2 K- B
- E3 I) b* {9 w; ~9 ` for u in nodes:; Q8 e1 w* M8 D4 s* V* f% V; |
distances[u][u] = 0
( E" A# i4 V$ c$ N% \! B for v, weight in graph[u].items():
7 g9 d9 A- S7 @- p distances[u][v] = weight6 ]- s: c8 V! x8 N( ~2 p* h# J5 e
, U* c' F& Q0 Q
# 更新路径2 S5 F# K- I( n6 B' Q( f
for k in nodes:, H* |, L( T; Y
for i in nodes:4 q# r) @9 j" g5 }/ }
for j in nodes:; v- z8 y! ]+ Q2 j2 X% [3 B5 Y7 |
if distances[i][j] > distances[i][k] + distances[k][j]:
. n1 o$ N. e/ g; k9 v distances[i][j] = distances[i][k] + distances[k][j]; z. C6 o+ q1 z, O5 F. M: ]" h3 l
0 H0 J6 N" Z0 ]
return distances% Y1 Z4 R! Q) g
$ v4 w. P. Y6 s/ T D+ \# 示例图(可以含负权边)2 N7 q# S k3 Q1 _8 l3 X
graph_for_floyd = {4 K1 r6 q1 ?2 u3 S2 {' j( ?
'A': {'B': 3, 'C': 8, 'D': -4}," Z F* w7 @1 R+ o
'B': {'C': 1, 'D': 7}, H2 e6 R! P0 c4 F/ o% p
'C': {'B': 4},) b$ Z8 e3 k: n9 U" x
'D': {'A': 2, 'C': -5}
. v+ D1 i- i! g; R6 N}
* o9 [7 J/ F* Q- ?. f% R/ w) Z, c$ a
shortest_paths_matrix = floyd_warshall(graph_for_floyd). J/ i' G( ?. Q. O( {, W
for row in shortest_paths_matrix.items():
* \; b m j4 j, g; Q- B print(row)
) f" d7 Z/ D9 U, d3 z```! E7 V# x8 X( e
5 m1 e2 ~1 z( e o& o7 E# j4 F
## 总结* y4 g1 Z6 Q3 p4 n o. Q5 ]
' p6 a" X7 `/ [/ `+ U# z
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
# } ^, i! |: w. d. X5 J- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。/ T4 E* B1 ^# {4 h9 a2 Z. H
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。
7 m; a9 t1 [4 G' W' Z! x9 Q; J, a* t2 |) @
不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!& P9 Y1 Q" ~4 g% _2 y4 q
& z0 f. @6 Y* W' l* Y
# O$ L4 [9 `4 A7 @* \ W0 z7 u/ P3 ~
4 P. y5 C2 ?# ?( ]% s |
zan
|