- 在线时间
- 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 算法。下面将介绍这些算法的基本原理和具体实现。3 p& e# D9 E7 o5 o
3 }8 M9 {0 X+ P% ?6 d9 f! Y## 1. Dijkstra 算法
; o, V" [: K1 |0 F; E S
6 f, M0 u0 X3 W4 _& ? WDijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
" {, W0 b# t- O2 i+ d+ {$ m7 Z9 \( h6 r. t6 T8 ~
### 原理步骤
6 k6 G0 v/ M- U. B; ~8 N+ |5 i7 _! C
1. **初始化**:
( ]; L. P* X1 R, j" I& M - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。
4 Q4 ]; g: |/ S% }' N0 J6 [; L - 初始化一个空的优先队列,用于存储待处理的节点。
3 ?, L- v7 c8 w9 w
1 l* E1 n9 B% y r! V2. **处理节点**:0 n$ K+ t/ N; ]! s- A( Q; e7 U
- 从优先队列中选取距离最小的节点作为当前节点。
% Y( i* @: L% G* a- v - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
! y0 O: o; I; c$ M# U$ D3 K4 C; N( X7 J& ^& |9 ]( `7 ?& a7 z
3. **重复处理**:' U" X. P' l: b- i; S3 r" c7 K
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。) ]8 c; e) @, Z& [; _+ R! W" ^, }
. K+ d$ P1 I$ B6 |: F4 V
### 示例代码5 X6 B# h. [/ p9 a2 Y
' m/ l/ @" z" V- I```python
8 V& y- Z5 U- {* wimport heapq
8 U( ~0 i' d9 Q- @8 ^8 x; \" X5 A0 q
def dijkstra(graph, start):
f; q; k3 r6 {! J6 M # 图的表示为字典,键为节点,值为邻接节点及其边权
; h& ?6 X1 l8 \* Q+ s) s' d queue = []9 o7 u) g; s1 {
distances = {node: float('inf') for node in graph}" U9 K) m) ~+ a0 C* b: a7 `" q
distances[start] = 0
' [ @( {2 x5 f4 Y9 I; C+ @1 | ` heapq.heappush(queue, (0, start)) # (距离, 节点)" W9 @* ~$ j& u% o) s2 j5 D
0 x7 C* s+ P& C while queue: u9 L j0 r) _5 Y. E+ n
current_distance, current_node = heapq.heappop(queue)! s$ [! O, ]/ L; g/ z
* \& _( X" y$ C* B, Z
# 只处理当前节点的最短距离/ X$ c, g1 \/ G/ f" g; O
if current_distance > distances[current_node]:
2 J5 i& M, D, g/ n- d! T+ X continue
# K2 D4 ?! A# \% J; ? U) u5 K# E( D. ?* r4 e5 R: I( h% D1 v
for neighbor, weight in graph[current_node].items():
2 f6 U% p2 V5 F/ D distance = current_distance + weight, V6 w$ a9 P6 |. X% U/ A
. k1 Q) K1 p7 l # 更新邻接节点的距离
+ G- a( U- B: s if distance < distances[neighbor]:
+ R; \0 w( t& F distances[neighbor] = distance3 t5 P. i' ` ^: V
heapq.heappush(queue, (distance, neighbor))3 J- f* E2 J; m* I
* \" \ H' b7 Y4 i( x
return distances7 D/ H8 s9 S, y9 a: F1 y j: G
2 ]- E% l2 n7 L2 M/ t# 示例图( g% J% U; e# I
graph = {
1 ]' V: M h" ~; K- V1 X/ G7 H9 i 'A': {'B': 1, 'C': 4},
: B6 S5 ~& U, k9 Y; W& x$ Z 'B': {'A': 1, 'C': 2, 'D': 5},* ^. O8 m5 J* H9 e
'C': {'A': 4, 'B': 2, 'D': 1},5 U6 e/ t/ l) _. q/ c& h J) N
'D': {'B': 5, 'C': 1},
! h) l7 S& B4 K3 Z P}
5 d5 W7 x+ E' D4 _- E1 m
4 V& e9 N- r' A% n) d8 m) Astart_node = 'A' n+ t) U% K, S6 M& H9 H
shortest_paths = dijkstra(graph, start_node)
" @0 O5 i7 Q$ k- uprint(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}9 u: J9 j- c1 ~# |2 S: e7 J; }
```/ W+ k* W. L8 c- H, ~' G0 t
7 e+ p' U+ S/ y& f
## 2. Bellman-Ford 算法+ c0 j3 c( R4 r/ t5 f
2 d' B& [6 a8 Y- T3 K; j
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。) t: h0 w4 g( Y7 j/ u7 g& |
1 H' v5 l! c& @7 z5 H& A' Y1 _" {### 原理步骤
+ y6 @6 @6 N0 N2 h) ?7 S4 _8 Z
1. **初始化**: V0 ~8 y& u7 Q; {
- 设置源节点到自己的距离为0,其他节点为无穷大。+ j1 ]9 Y1 j/ J( K |! v
2 N/ R+ r6 ~9 g; m
2. **松弛操作**:
- Q' c; J; j+ z: n - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。
) x J5 I3 K% b% }' U$ k( Q: J9 H6 N% ~
2 S+ C8 l3 I7 r$ ~3. **检查负权回路**:
3 |8 H0 O7 t. g4 R - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。" h# b& D" q4 X; l# N' x# k7 x
6 t5 b: M, `0 z4 K- A4 a### 示例代码
* H* [1 l% o8 N3 G- d0 e. X- a6 M. o# F+ T- e7 w
```python6 N7 l' h9 Y! m
def bellman_ford(graph, start):* f" x! d/ \- d: l- \9 J, T2 k
# 初始化距离
: m3 @% g3 r8 ^0 i0 @$ m distances = {node: float('inf') for node in graph}
: ~0 ]4 @ s2 ~5 m/ n/ P8 { b distances[start] = 09 R! N, }* i: P9 c& b
9 u9 X4 ^$ t+ h
# 松弛操作
6 v5 ` }% i8 s8 W for _ in range(len(graph) - 1):
; U4 G4 Y7 r/ M for u in graph:9 Y3 Y, G5 t0 s8 |1 [
for v, weight in graph[u].items():
% f/ z( a( D t- u' d& t if distances[u] + weight < distances[v]:3 P5 [! n R( X4 K9 f' o$ q8 ]; j
distances[v] = distances[u] + weight
0 I8 { z9 z/ g* }6 }. j/ I, a# I+ v& s
# 检查负权回路- W+ I E( r! I, ?) T) n+ U
for u in graph:8 g: R6 c! r) `1 h4 [4 N
for v, weight in graph[u].items():
6 e/ E7 U% ~6 g) l if distances[u] + weight < distances[v]: h; j: d3 X. @+ K5 F/ D) P/ [, n
raise ValueError("Graph contains a negative weight cycle")
* ?; B8 k( R; z. t' w5 x# O& k
return distances* e; H$ f5 B' j" h0 N% x
! X' u4 F; k2 L2 Z$ \1 M7 T4 p# 示例图(带有负权边)
/ a4 |& }' B- C3 U% Ygraph_with_negative_weight = {
" p* _; H+ T" ?8 { 'A': {'B': 1, 'C': 4},2 a( W& q* o2 p$ D; f* B. Y
'B': {'C': -3, 'D': 2},. s1 G0 G& M8 b* |- F
'C': {},# L8 X; ?( J1 k! J* T$ D
'D': {'A': -1}
" c! T7 ~% V3 i% K}
9 Y8 U& _' h5 ^- u6 C: T% q- a! V! {1 L! g! i; A0 @. ]3 \
start_node = 'A'
0 v9 G( ?( T% u+ \shortest_paths = bellman_ford(graph_with_negative_weight, start_node)
& y/ T( f9 U+ f. ]% z; Yprint(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3} X* n6 [% \. m) }( {; u# g' V
```
x1 h8 O$ N7 N3 i7 i
3 }4 g8 D$ Y0 z% |## 3. Floyd-Warshall 算法
9 G0 N4 q$ C8 d% g0 D2 x! N& d# t0 x6 L0 q# T
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。
- b4 ~ j u9 U `! E. h$ |5 }, Z2 z3 l; m
### 原理步骤
$ q! }- [8 ]1 F8 M; p w9 g# A" K, R4 W0 ^
1. **初始化**:
1 J$ R% ^( v! z. P1 e+ M0 h {* \% y - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。3 W8 T1 I" I$ ]6 T# v3 m% W6 j
; a& {: H$ ?# S0 ]) o
2. **更新路径**:
' q( A& Q& L0 M( i- t7 ~1 F - 三重循环遍历所有节点,以每个中间节点尝试更新路径。1 x( ^! I- a C9 Y' y
: Y" Q$ j$ ?" O E3. **输出结果**:
9 A9 R0 W8 R. @% |* Z - 最终得到的矩阵即为每对节点之间的最短路径长度。7 \+ b0 y3 v8 {# E3 j
/ }6 {% h& Q& D6 `( Y
### 示例代码
9 Q% g$ P- K @) ~6 }: \: P5 A3 S( [6 K$ |$ n
```python
* g c2 I" D5 h7 u% g% t [" mdef floyd_warshall(graph):' o% x% i. P" z3 E5 x
# 初始化距离矩阵3 E z( ?$ i" `. {1 q6 y( Y0 g
nodes = list(graph.keys()). U' g; w& @& p# V+ f5 ?. F
distances = {node: {n: float('inf') for n in nodes} for node in nodes}
. \7 y0 { `: |% S* b' d$ {* |- j; C# F A1 }, a; a
for u in nodes:8 s" K9 \/ ^# `) H4 z3 s" Z5 Q
distances[u][u] = 0
& U+ Q& _7 x% @. f2 k for v, weight in graph[u].items():
' S1 F6 ~$ l, O& V7 _ distances[u][v] = weight
7 c' s2 d5 J7 g6 V/ U2 n
! T! } \+ ]8 R$ g1 T* ]; `' f! @ # 更新路径
! z! w8 z% H& z- P z* I3 ~ for k in nodes:
* k' B$ ]% J. j0 D1 L0 B0 v$ @7 q for i in nodes:( i: E! ~9 T9 \5 p$ G7 q
for j in nodes:
; E% ]# J6 f2 J6 ^2 E7 i# Y$ u: H if distances[i][j] > distances[i][k] + distances[k][j]:
% |6 |( |2 U' E4 E- c. \: t distances[i][j] = distances[i][k] + distances[k][j]! `) ?8 c! h. P0 i) ]/ _* {8 L
5 `2 d7 l& k# W0 C# K' M
return distances6 P& X7 ^' J# H; R& m; _% E6 r6 f7 B- N
- ?& u/ S& M& w7 y8 s# 示例图(可以含负权边)- ~- Z' V( K# V: y2 J. g! \4 f* S
graph_for_floyd = {
- O- {, u2 o! ?7 t 'A': {'B': 3, 'C': 8, 'D': -4},- a6 q9 R: V$ S) n1 F
'B': {'C': 1, 'D': 7},. |: P; h+ I/ T8 y% Z. H5 h5 h
'C': {'B': 4},
j' x; o; {- E 'D': {'A': 2, 'C': -5}1 U5 B- \: N9 `1 |" V {. {( @0 G' o
}
& ], K, g. [1 |+ h
; [! l, J$ w9 n9 r4 D& wshortest_paths_matrix = floyd_warshall(graph_for_floyd)
. f" U1 Q1 u- Ifor row in shortest_paths_matrix.items():
* v* J! X4 g$ v" d7 q print(row)* [4 k! V. L4 c& S6 x4 {- n( d# u
```9 U6 N4 u: K0 Y* e
: T) l: B# b2 t0 I7 W; N
## 总结1 V/ k$ x( [- F; Y8 T) j
4 w, R- r- `* B- `# u
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
- h5 t5 }! E1 z2 }- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。
( M% N, w- L0 D3 `* j+ q0 `. W- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。
- E" s8 P+ Z3 D& h- J; J; G' a6 C% F1 @; w3 u, t/ A5 s
不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!
1 D9 K5 ]' o& r) m8 c. p! h* m/ s* P! |. f4 \7 R
4 z8 ]: C! A+ B
7 q+ ^; n, o F! m }9 `$ i |
zan
|