- 在线时间
- 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 算法。下面将介绍这些算法的基本原理和具体实现。
1 t. \! H% @2 F: S& \, ]
* e( b' `& s" j## 1. Dijkstra 算法
* @% K; b* x; q* E# g! |" J% u5 y3 m6 W, H9 _$ t, U" ]
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
: Y2 d Z1 v: w' Y( c
# b* [* i% J( z2 |8 @" H: u### 原理步骤1 l- q& o: a9 i$ e$ D- M- E/ O
1 u6 S, h5 C* v1 f3 d2 c1. **初始化**:0 k; u! s, u/ `1 J2 c* E5 v
- 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。
0 _. g( ~/ H, D# ? p - 初始化一个空的优先队列,用于存储待处理的节点。
5 i3 J% T% ~, f9 I. C5 P
, g# s; Q! j1 f& @9 `2. **处理节点**:
4 `3 B7 V- t' e# y8 P - 从优先队列中选取距离最小的节点作为当前节点。0 b8 a0 i6 l/ Q/ ^8 e
- 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
5 g0 R# y1 M4 N' ?* C6 ]$ u A# g3 R& J. S/ q
3. **重复处理**:
7 ^! D9 F0 l& p' s! a$ Q - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。 j; p/ l0 G# E; ^# C( v( y$ g
! i7 c: u P2 Y+ R### 示例代码
( q' K' C: A+ k- F8 Q( m' o k' q* c# x1 }6 ?; I
```python
& \1 y6 u1 x2 B8 simport heapq
6 k# I, }8 [1 }& `/ {
, y, s4 @% K( O1 fdef dijkstra(graph, start):
# B3 N6 G2 G5 D4 ~3 c7 b # 图的表示为字典,键为节点,值为邻接节点及其边权
2 \+ f& d0 r) q7 D queue = []/ o; D/ p. b0 d. r" U% b4 y
distances = {node: float('inf') for node in graph}
3 ?) T) w; \6 e# k; x distances[start] = 0
6 p3 q3 U2 W( J! M& L0 o heapq.heappush(queue, (0, start)) # (距离, 节点)
4 a( `, d4 `* m q
* U* _$ q3 M/ z* i6 v% Y2 Y0 ^ while queue:
, ?, x' K- I5 l1 @- P current_distance, current_node = heapq.heappop(queue)9 `" l; @0 g0 ^7 B! k3 S" M X4 l
: e( m, Q# w' b; L" B8 C # 只处理当前节点的最短距离
6 @; a4 {+ h& W5 h if current_distance > distances[current_node]:1 f3 f$ |7 b, u# x; a
continue
! C4 |; i) B' y9 t* ~
+ ^# A8 Y( J( Y0 b9 V! H for neighbor, weight in graph[current_node].items():
9 {. N: m( F+ z; j: g! s) U5 q distance = current_distance + weight+ P `% Z g, C) G# A$ F3 M- J* {
# @1 Y0 \8 s: s9 ?/ E h L. o$ i2 `
# 更新邻接节点的距离9 Q1 P s! F- b* x
if distance < distances[neighbor]:
2 s+ c6 X$ d3 |* f9 r# e distances[neighbor] = distance" T$ S7 E' d4 W5 \" q _4 C X: `
heapq.heappush(queue, (distance, neighbor)): y- O( F$ C: g: k% z
2 H, p& t0 A* W, H6 `7 c
return distances
0 W6 }% q+ f, r9 W
/ o0 n+ A" G) y* H7 Y& k$ y# 示例图- w% Y. ?4 r9 c0 H
graph = {
6 H- x& D& p4 V% e9 o 'A': {'B': 1, 'C': 4},
. M( Y# X+ Q* j5 I 'B': {'A': 1, 'C': 2, 'D': 5},. U% B3 z- r K( ?" O+ r
'C': {'A': 4, 'B': 2, 'D': 1},
8 Q3 `$ V2 w) b! [ 'D': {'B': 5, 'C': 1},* ~; I: E& n# r0 I2 W. Q
}
7 f# \ c4 ^% {6 H& {- k9 n4 N. H% U" ^
start_node = 'A'
8 v+ B5 e6 h3 W4 w) N4 @8 Y# S5 G0 C" f1 fshortest_paths = dijkstra(graph, start_node)
3 b, }. C) _, G* J0 L/ j; Sprint(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}8 P1 a3 J& x( N" B2 F& X
```1 o: G; H6 r& _
q) w L& q3 Y$ X1 e, u( T
## 2. Bellman-Ford 算法
+ y6 [, F9 E; @* O1 p2 J8 `! Z G* v8 V u
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。
V* q' [ o! L5 B; U& I" t3 |' E6 u2 i
5 P2 h r d- m" V0 Z### 原理步骤
' c2 v2 W A3 S( S$ A& J6 g) e1 E6 x8 `; W" u3 @9 ^0 [
1. **初始化**:# |. X& S1 n1 K: I
- 设置源节点到自己的距离为0,其他节点为无穷大。
% U8 e9 Y/ c, H) N* h d1 U* I, H! }* ]8 |" M" C
2. **松弛操作**:
- g6 Z. B: B& g1 @/ ^ - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。
2 P( g+ T! V& f! `4 f
7 _/ U& G/ Z9 s+ x1 ?3. **检查负权回路**:
. l2 I; ~8 L/ v( s0 F7 x K6 }, m - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。
4 G V3 @# L# K3 c5 Y d' J3 y
. I; N/ h- d; r### 示例代码
6 L5 ]$ t$ @0 r q! _0 H9 h; b2 M) F8 V2 o9 j, l( N
```python) k! m6 Q g! ^- ]: R
def bellman_ford(graph, start):
/ h1 T& P3 U" ^ x2 }1 |$ p # 初始化距离
2 A& M' t( i/ E1 d distances = {node: float('inf') for node in graph}
5 q- l c+ h5 S1 R; b distances[start] = 0! v4 s w& L8 r) q+ z$ }. I: w9 u* U
% b: a: l" r+ s( R, J # 松弛操作
, q2 ]9 {: q. g4 v, T0 N+ u- ~ for _ in range(len(graph) - 1):5 O s4 S0 U& u. T
for u in graph:
2 G+ ~+ l U% d. n for v, weight in graph[u].items():
; W) X: Q6 S. |3 p! d4 V7 d4 Q if distances[u] + weight < distances[v]:
i/ ~+ M. U& f9 T6 L; Z distances[v] = distances[u] + weight$ R, N5 i! ]( q- [( ~/ Y
/ B7 L& Q0 I" K1 s" b
# 检查负权回路
: n7 w5 \$ s! S/ V for u in graph:
5 m: E" {( e1 l" a5 @ for v, weight in graph[u].items():2 N0 y! H V! p: [7 x* x9 O+ Q
if distances[u] + weight < distances[v]:. s9 d* T7 U! V$ M/ {+ v
raise ValueError("Graph contains a negative weight cycle")+ s6 L- h6 q* F: {0 l* u
0 w5 G! u L6 A, q5 o7 d h
return distances
: |$ t) ]5 g) Q: z( P7 K, ~0 a+ |, x# O% J& ]; ~3 O$ P% E9 j+ I3 R; I- f
# 示例图(带有负权边)( Z* h6 h3 q8 ^5 ?7 l+ i6 m+ J
graph_with_negative_weight = {
% G" G' i. B' V4 s 'A': {'B': 1, 'C': 4},
9 u3 G1 p; c& O, A5 a 'B': {'C': -3, 'D': 2},: w; X* d$ Y4 d, o7 m4 ^
'C': {},
* ?) L6 w9 }& }6 g/ J/ [, l 'D': {'A': -1}' v5 O4 h/ P* T/ [9 x9 |
}* f8 ?: b* O7 U) K( h& J+ d* O( h
6 C6 S {- Z1 v G% S& A S' lstart_node = 'A'1 D( S' i' u7 N4 c
shortest_paths = bellman_ford(graph_with_negative_weight, start_node)
9 g5 |: j- l0 s) mprint(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}
$ u. ^: r, L [- k```
6 w- J) {/ ~% H$ J& O$ a$ u0 i/ S8 `+ o
## 3. Floyd-Warshall 算法
. x( U0 @% M! _6 a) |; E( |# K X+ L0 ~
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。0 v! T. a* ~. V5 h7 H# b/ }# o# L
* e6 z( V% ]0 A$ j/ a
### 原理步骤
0 K; M& X+ f2 s1 T
; i1 H5 M1 G, u1. **初始化**:
/ D$ W5 ^2 D: S Y2 { - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。
: d" A( K Y4 S0 h' Q M* ^7 M* [$ ?- r2 g5 _- o
2. **更新路径**:
& o" h. F2 o" O - 三重循环遍历所有节点,以每个中间节点尝试更新路径。
; Y& a/ a" k+ G! _
6 t" _2 i4 z+ ?6 K! Q! m! @$ {3. **输出结果**:
( _& O, [0 m0 [% ]$ T. @- Q - 最终得到的矩阵即为每对节点之间的最短路径长度。& |7 g3 P3 M3 @+ D: z
" M: [. R& v0 e) s( R% o### 示例代码
/ ]' r4 {4 r" x- }. m) o# E V6 B! ^& x* \2 m
```python' G( k" V$ D( x
def floyd_warshall(graph):
+ V3 g u7 ?; W0 h6 O # 初始化距离矩阵 j" |9 [. n+ O' F. P
nodes = list(graph.keys())
5 }" n# G( R6 o. w, V distances = {node: {n: float('inf') for n in nodes} for node in nodes}7 y+ e7 k9 x9 \* V4 M& `* S/ Q
# K2 M y' r1 o* |
for u in nodes:
9 \$ R" i$ ~# B" `! B, h distances[u][u] = 0
( L2 g' Q1 p8 p& \! Y5 r, h& ?& [ for v, weight in graph[u].items():
* I4 r" `/ O+ p2 m! f, o% u1 c distances[u][v] = weight
9 s1 z6 }% T4 i. U2 r
; W+ [( o4 w& b# V # 更新路径
$ T* k0 \9 ~2 {* d) d for k in nodes:1 v! f' G+ C- u0 H% j
for i in nodes:
2 }1 f, Q8 ?5 a' [1 q( |: S5 G for j in nodes:9 }% Z1 l% W/ u' h1 o
if distances[i][j] > distances[i][k] + distances[k][j]:
. c" N5 u& T; F; r4 p distances[i][j] = distances[i][k] + distances[k][j]; w7 P) g9 A7 |0 Q! M$ Z! h
; F/ g' i& J# M! {" T0 { return distances
& r# z+ b* G; V; I+ f1 V
6 J: T# J; C4 X, s, ^$ u4 w# 示例图(可以含负权边)
) j! o- I, x! E8 p" mgraph_for_floyd = {
& @! i$ j, _, T4 E& @ 'A': {'B': 3, 'C': 8, 'D': -4},$ @- @) N H% R: Z
'B': {'C': 1, 'D': 7},
6 f' ]& k9 v- C, z( [7 T& T 'C': {'B': 4},2 ?! \. t' f: Z B
'D': {'A': 2, 'C': -5}. |' J6 N5 H) j- o
}
% A: t$ V+ G$ K/ Y4 V
2 J. L$ X! `7 a0 e, d1 b' v' v, L) dshortest_paths_matrix = floyd_warshall(graph_for_floyd)
( P5 C* t% [, ^8 ^. _' @/ Hfor row in shortest_paths_matrix.items():
3 ]) a. O% c4 ]6 h print(row)
% Z2 J) H2 M: _7 N. n4 m4 K```
/ @7 p: X) x) }# z! f; W/ Q: L
## 总结
& M" i% l. `3 I+ N9 N B5 Z
7 b# g! r( L8 x( s3 d s- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
6 y( n& s0 I& Z0 r1 \# x7 ~1 L1 P- d- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。5 b& J* P: K, U4 }
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。
' T9 z: c) A1 L; }, C% B. G) {! s4 @+ {: a$ J: K( |, M/ E
不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!0 i/ E: h0 u/ t! P. ]( g
* h$ |1 _$ L. y( F: ~
; l) Q: N& x4 @* l O* N2 Z2 T! ~
' R3 G6 a/ I3 O P5 h% y3 P |
zan
|