- 在线时间
- 469 小时
- 最后登录
- 2025-8-1
- 注册时间
- 2023-7-11
- 听众数
- 4
- 收听数
- 0
- 能力
- 0 分
- 体力
- 7563 点
- 威望
- 0 点
- 阅读权限
- 255
- 积分
- 2849
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1160
- 主题
- 1175
- 精华
- 0
- 分享
- 0
- 好友
- 1
该用户从未签到
 |
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。
6 ^4 @+ V) h: Y" o6 z2 c( M
' ], o5 A/ w7 L a## 1. Dijkstra 算法/ m) S& o9 }3 _, @) k
5 K9 f' h* N3 \$ _ ]
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
6 P) V% |6 U/ e7 J$ u5 V0 g4 M6 Y7 e& S% D; |* c1 ^7 m
### 原理步骤
5 j) a& b# h" N7 p ~# F. S% R
1. **初始化**:
3 W3 r; Z2 o) ]# Z, K8 I - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。
1 |6 V, t3 t% h8 |; B6 o7 @ - 初始化一个空的优先队列,用于存储待处理的节点。
2 q+ }: I0 A5 y
' g+ d" |5 Y& G2. **处理节点**:/ y" y5 e! t1 M1 `( N8 _0 O+ d
- 从优先队列中选取距离最小的节点作为当前节点。
$ c3 Z, \$ S" Y0 S, ?# R - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
) T/ w" E9 I6 I' `3 T' O/ |, |
0 j) ]! D V& `8 Z) v/ B3. **重复处理**:9 w# ~ _* z0 x* W
- 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。
5 s8 x1 F- s. G3 ~
: j! J$ w6 p4 t, j5 Y### 示例代码8 O- c& V, n$ L& f$ A2 s/ M! w
5 t! @* @/ W/ n2 m9 ]. s* N```python# U: P+ V1 g- X _$ S* D- E$ r. b
import heapq
% b% W# O. V; y2 W$ y
M Z6 s( Q0 o. Wdef dijkstra(graph, start):
2 t' T( I3 ]9 | ^2 f2 b # 图的表示为字典,键为节点,值为邻接节点及其边权, N5 N$ l! S# _6 U e0 K8 M+ ]7 W
queue = []
7 @. _* E& `4 K/ L distances = {node: float('inf') for node in graph}
1 E5 l7 p* q5 u& w% P' p. E distances[start] = 0' j: }4 q- t: ?# y
heapq.heappush(queue, (0, start)) # (距离, 节点)$ @6 f! a6 l+ z {9 z
) }$ B( q- d, I- n3 ?3 K# @( }
while queue:
) O# |$ i; k% H1 U( ? current_distance, current_node = heapq.heappop(queue)
" R) ^! B: O; q3 o1 D9 j% n7 A* j! W. C5 b
# 只处理当前节点的最短距离- y: ^) \9 O5 O1 {+ ~- h9 i' _3 Q, R
if current_distance > distances[current_node]:
4 c) e" [) K& P7 f! ]; g0 v! L continue
+ r( y1 _& @+ I
! [+ p0 V6 E4 k& }6 t z, V for neighbor, weight in graph[current_node].items():
# [3 p m; S9 l, ` distance = current_distance + weight# S/ Q6 c* N, x$ I f8 ]
8 v- t& \% X' a
# 更新邻接节点的距离; ]# l9 g! o" x! c) K! D
if distance < distances[neighbor]:
- j G" Q4 e9 [/ ] distances[neighbor] = distance
& d1 I h( V0 {9 V/ |! K1 r heapq.heappush(queue, (distance, neighbor))
! `! X9 Q! r j
, |( X1 P$ R1 m ]. f return distances9 F& A& Y: J1 [7 _: H) Y! F
; w- A! W |: }8 n2 l2 N/ M4 u/ E+ t
# 示例图7 R7 O( D6 S& Y! w
graph = {
[+ \" k$ c- p6 O$ T& [7 E 'A': {'B': 1, 'C': 4},6 c2 P5 k! q& k6 u: U& Q2 X
'B': {'A': 1, 'C': 2, 'D': 5},
% r* L5 s- ]2 _% |7 w 'C': {'A': 4, 'B': 2, 'D': 1},
4 x% s0 _# K* {, E/ n/ K* R3 O! { 'D': {'B': 5, 'C': 1},9 W7 [2 q- ~! n5 k0 P2 p. k. H
}
) t- F! V, V2 }0 q5 W* S% J4 ^
. B2 V. ]2 J3 j* @$ v8 c$ k( Zstart_node = 'A'! o/ F: K$ N( k8 A/ {
shortest_paths = dijkstra(graph, start_node). L6 P6 h+ e' P- D
print(shortest_paths) # {'A': 0, 'B': 1, 'C': 3, 'D': 4}( j% ?6 R! l, l; F3 Z$ ~
```: _1 Y) A, e( u0 [- U, f6 I5 H3 j- {6 H
" y' ]/ C' c- E! s2 H
## 2. Bellman-Ford 算法
# L% E; F* C6 l% m8 Z' V! C4 K" ]" o
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。
0 a3 Y" S/ J$ K: z' y3 M; H6 v% H! k
### 原理步骤
& l& b- ?6 p) W, {* L( z) C$ W" e. {! {- X2 }9 x" H
1. **初始化**:; {$ Y1 H: g3 A: r# m
- 设置源节点到自己的距离为0,其他节点为无穷大。
( s- P* a1 I# p/ n* e+ g& e* X7 S. \2 |4 S
2. **松弛操作**:5 R6 w8 H0 d* v6 _8 }0 _+ S
- 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。: U' Z5 @( _4 M2 V" d4 p
8 T9 ^: B z5 b: }: t$ D) n
3. **检查负权回路**:, {2 t) n8 p6 \
- 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。
: G: ~+ S& I) C D y: X8 R% Q' } _. U" h) B3 ~1 {) }0 v( g# H7 n
### 示例代码
( j# ]% |+ N3 g
2 p0 C* m+ O e; N4 ?* h: \% v```python
( P' u; Z' E5 l1 M: p, idef bellman_ford(graph, start):
/ Y! [% F e7 A& u C- R # 初始化距离
' f' x4 T o% U: W8 q distances = {node: float('inf') for node in graph}/ ^4 ~& e7 h! t
distances[start] = 0) q& Y7 c# J! C2 G8 m' z
: s/ B4 d9 |* }! T* h. p
# 松弛操作
|1 v& Z) ~ | for _ in range(len(graph) - 1):) i2 r. \9 f$ B& z- n' Q
for u in graph:0 |4 R! M+ h! \ p& W4 N# K
for v, weight in graph[u].items():( j! v5 g; P6 L9 a
if distances[u] + weight < distances[v]:
7 [/ x7 D/ P1 h; o distances[v] = distances[u] + weight
4 f8 `5 x- |1 \) b* \3 u, E5 z% m. D1 _3 }- k$ O) [5 Q+ }
# 检查负权回路
3 E2 e" s. q4 C8 E) S0 R% l for u in graph:
i% a9 s6 C: z for v, weight in graph[u].items():
; ^ v% A1 H. y3 `2 @- a if distances[u] + weight < distances[v]:# C# p, d. @6 z9 y: r; ]* d0 s1 v
raise ValueError("Graph contains a negative weight cycle")
& a6 [4 i7 S+ E; C+ X7 n5 P/ z; H$ m5 p2 Z/ ?0 @
return distances
, Q" b2 X: h+ V* ? L; ~% i& W' T( f1 q5 B3 h
# 示例图(带有负权边); t0 {' M A3 o' e0 {5 K
graph_with_negative_weight = {
" n+ S" _' j; G. P, V- N$ Y* X 'A': {'B': 1, 'C': 4}, h% m* Y" c+ }; l8 N! C6 U
'B': {'C': -3, 'D': 2},6 M5 n* C! a" {; C
'C': {},$ x4 j( r) z j' W& P1 H
'D': {'A': -1}1 Z5 v5 I# [( p# S+ ^' |
}
3 P/ z, H- A9 I! M7 R" F0 `9 L1 V% f- R
start_node = 'A'
! z: s( l; Y, Y- R$ q# V4 G+ [1 Pshortest_paths = bellman_ford(graph_with_negative_weight, start_node)
# `4 G6 c8 n& a9 C% ]print(shortest_paths) # {'A': 0, 'B': 1, 'C': -2, 'D': 3}$ i' U2 h( ~! a2 k6 O
```
# A( {) T9 P- I% a4 P, `0 L, `( R* S0 g, t- L$ F. q
## 3. Floyd-Warshall 算法# x2 [5 ^% w- x- T
0 j- K0 `; l( P7 e
Floyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。$ M2 m' m: R \) _, m% V' b: E
/ [' [7 G- |) U5 U, j' U) E
### 原理步骤
3 M% X$ z2 z" g. T- L# \5 Y6 T# E" N# q# d9 s
1. **初始化**:
g+ a+ P% X6 v3 h( k8 U7 v - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。
) \4 N5 L) j) B% z% h9 @) b
) g7 \. Q0 f, U8 g2. **更新路径**:
+ V% ?% G, q) M- j' W) q* T - 三重循环遍历所有节点,以每个中间节点尝试更新路径。1 |7 J& b$ j' G& m' d) F
0 X6 ^* X0 r& z6 @9 G6 j3. **输出结果**:
# \! z& Q1 Z$ p) z# V `8 v; a - 最终得到的矩阵即为每对节点之间的最短路径长度。
% ~9 o: U. u) s( Z. ^
# b7 z5 Z4 Q1 k, @ Y3 F### 示例代码/ _# J! l! f/ f/ h# a
* w. y. \$ u* i9 C4 E$ V```python' c# t0 R- G; U p% k$ s
def floyd_warshall(graph):. t4 ^) ]/ u) Y# |, b" x; v& ?: f
# 初始化距离矩阵; u. x$ B8 d, V7 B( o' R
nodes = list(graph.keys())
) H* m& @& M% _: [ distances = {node: {n: float('inf') for n in nodes} for node in nodes}, R9 t; r5 h! B
' n, I& B6 S& b+ f; {" }% W
for u in nodes:
; g3 \7 Z. f1 s7 t4 g2 E1 a distances[u][u] = 0
+ O3 a6 o8 n5 }5 [4 l for v, weight in graph[u].items():
' |" U+ j* B0 C n( X+ ^0 ^: c distances[u][v] = weight
" z, v7 l& ?9 s
. j: _& M! f* H0 V # 更新路径6 z" S {- \0 |
for k in nodes:
8 w: Z1 X; O7 O! v; C9 _ for i in nodes:4 I; h5 I$ k: x! {$ L9 X! Z
for j in nodes:8 g- i& s2 C( \) J. ?
if distances[i][j] > distances[i][k] + distances[k][j]: W( Z8 V0 S9 o" @% J/ K: V
distances[i][j] = distances[i][k] + distances[k][j]
4 F9 W# w! a: }5 I0 r- d" N3 ~* a6 o! ^' s e5 D
return distances
8 x3 e' F9 @6 W2 B/ b( k" _ k. w& `. w) P7 p' ?9 P
# 示例图(可以含负权边)
) V: {# B+ s m, z/ Z2 x, ?graph_for_floyd = {
8 T+ w7 @1 N: j3 K 'A': {'B': 3, 'C': 8, 'D': -4},
; y z6 ?8 f3 p! b 'B': {'C': 1, 'D': 7},0 d+ v0 F2 p0 `) B6 j: o" y+ b
'C': {'B': 4}, }: _6 {$ l: _# d6 }5 N. w
'D': {'A': 2, 'C': -5}
: A" z- Y" O3 S- d$ ~0 ]$ J}& L" m; G [; K' L3 f6 Z c2 C
$ k* s- B+ P L/ B# ishortest_paths_matrix = floyd_warshall(graph_for_floyd), n. \; B8 S& K* W' v; k
for row in shortest_paths_matrix.items():
0 @* Q. ?3 V# G print(row)
: a8 f, a1 q, |4 {1 p8 k3 t8 m1 r2 f```
1 K0 D, ^/ N+ h% L: V3 p- k2 i: f% G' y" |' y$ t0 {7 |
## 总结4 T; v: m" h1 B9 F
5 N7 d. r: \0 T8 u- s
- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
( d$ Z, L; q; d/ w ~$ m- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。; l5 F; q8 q& B% r/ y/ |$ V
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。, q2 ]: A. Q$ f0 k% l5 @6 K% s
* o5 ~0 P2 b- |! A" [不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!
( r% L( l, ]; s1 X" F' O8 \% ^7 p9 T' s5 O4 @, Y/ l" [6 k
6 v3 L6 k7 {# E" _# p3 B9 Q
, h. ~: p" z- r8 b* H7 n, I |
zan
|