数学建模社区-数学中国

标题: 最短路径算法Python代码 [打印本页]

作者: 2744557306    时间: 2025-1-13 17:26
标题: 最短路径算法Python代码
最短路径算法用于寻找图中两点之间的最短路径。这类算法在网络路由、地图导航、物流和许多其他应用中起到关键作用。常见的最短路径算法包括 Dijkstra 算法、Bellman-Ford 算法和 Floyd-Warshall 算法。下面将介绍这些算法的基本原理和具体实现。! j* I, h0 @3 k8 q6 D8 {
$ @! k' o+ e# s0 x7 H
## 1. Dijkstra 算法
( B. V' u2 n& h# J+ z5 A# c% `1 P; z* O+ T% g6 H1 j/ L, ?+ E# z
Dijkstra 算法用于计算单源最短路径,有效于边权为非负数的图。它通过贪心策略逐步选择最短路径,从起点出发扩展到其他节点。
0 n9 `6 O8 k& g7 _* j
' L% B  O' n8 k. F0 x! r### 原理步骤9 f# f* v. }# C5 T# Y6 v
7 x: o  v+ t+ l; A3 I* G
1. **初始化**:
5 v4 N" N! R  ^. \7 @% H   - 设置源节点到自己的距离为0,其他节点为无穷大(一般用 `inf` 表示)。* s9 x$ L2 |6 k$ h* i5 C
   - 初始化一个空的优先队列,用于存储待处理的节点。
4 ~$ d1 q# A/ m( L. b& ^. X" D" {8 P: `: D% o$ X4 J' X
2. **处理节点**:3 E5 q; u! ^' `) H+ T& E0 C
   - 从优先队列中选取距离最小的节点作为当前节点。
- W6 J" \. Q& h3 F0 O$ l5 j   - 更新当前节点的所有邻接节点的距离。如果新的距离更短,则更新并将邻接节点加入优先队列。
$ F3 m4 a( K9 n0 L! J8 Y7 h) q1 k2 H7 e
3. **重复处理**:! \# ^3 Y* o" l" L5 ^
   - 继续选择距离最小的节点,直到处理完所有节点或找到目标节点。6 D+ \" g9 Q9 [" `: R: D2 v  ?1 y

. ~% H  X2 \& p# j  H* c, T0 M### 示例代码0 ?5 M- s+ Q8 N1 M2 w8 n- J% Q
" }6 H2 c5 P6 q( G; P# k
```python/ S1 t0 a0 F) X& h( L: o
import heapq
$ F$ T! S4 g3 [
% ?) Q2 ^1 ^" s$ p& n3 hdef dijkstra(graph, start):
% X! Z1 ~. p) _# Z  L1 d    # 图的表示为字典,键为节点,值为邻接节点及其边权. k- s$ g; Z6 A
    queue = []' f. j) n% ~& R
    distances = {node: float('inf') for node in graph}2 {% u% R$ Q- e4 c: z& d# e
    distances[start] = 0
9 ], T  n3 E. t' `    heapq.heappush(queue, (0, start))  # (距离, 节点)  S! l3 V6 ?. K: f" P
9 E) k% i1 X7 D# \* \* a, b
    while queue:, C# M& C9 [) [# f; i
        current_distance, current_node = heapq.heappop(queue)) Q5 w( D' x$ i; Y
; S* V* f, }6 U# {! _
        # 只处理当前节点的最短距离: E: ~7 I3 a5 {- e  h( r
        if current_distance > distances[current_node]:
# I+ s/ W. S0 x& Y( f* D: [            continue
% J. ?  ^, g5 m5 V( s/ M9 L8 V
+ l" ^+ Q- w/ V8 T! C  i# S/ ]/ {4 O        for neighbor, weight in graph[current_node].items():
( ~/ T$ U8 ?3 E8 g2 |* x            distance = current_distance + weight
, I6 E1 z  `) C7 V, P9 J4 q
$ `+ s5 }& H% R% t            # 更新邻接节点的距离
+ N# i! D) H9 y- E* K/ ^            if distance < distances[neighbor]:
' @% `4 t- `+ i( R, {; e                distances[neighbor] = distance
5 z) `( ?3 l! l! }% d( Z                heapq.heappush(queue, (distance, neighbor))
) _2 e$ H: F  a$ B& Q$ t8 X6 `# o! w8 M8 Z* t: q
    return distances, M- m3 L8 v! C. e
" J& r' L3 h: g; Y3 \4 ^) x6 L' h
# 示例图
/ ^. E& x$ g" M9 g/ {8 \graph = {
7 o) Y2 w6 Y8 y2 [8 Y    'A': {'B': 1, 'C': 4},5 C; M+ F8 Y" U
    'B': {'A': 1, 'C': 2, 'D': 5},  A$ G- ?% f- D" l* @
    'C': {'A': 4, 'B': 2, 'D': 1},
3 j; Y6 K. |% m$ ], [    'D': {'B': 5, 'C': 1},
" Q9 \  n! Z9 }2 T  a& V}% A8 q% |8 F5 f' ]

( w7 X5 x* G6 J( Astart_node = 'A'
* A# M& l& F" k9 a5 Tshortest_paths = dijkstra(graph, start_node)
, S1 t0 z7 {( P; R  lprint(shortest_paths)  # {'A': 0, 'B': 1, 'C': 3, 'D': 4}3 W$ B3 y; w* q# A. o9 z
```
# d. D8 R: H* g+ ~9 w- ]; k" }
! E* d; h! G$ F- I0 H7 ~& l## 2. Bellman-Ford 算法  k% V! |9 |9 F9 B
* A; M/ A1 c  y
Bellman-Ford 算法能够处理带有负权边的图,但不能处理负权回路。它通过放松操作逐步更新从源节点到所有其他节点的最短路径。) a! s$ |3 j3 x# b# r
6 y+ {" s" `3 e0 n8 Q) m
### 原理步骤
, O+ ~; w, K) a* X7 D2 U" J8 ~* t( R. y  B
1. **初始化**:
0 M" h* u3 M% X; i3 ?   - 设置源节点到自己的距离为0,其他节点为无穷大。
& u  v" E& c7 V8 K; h2 i* c! E0 W% s+ @- B; z# ^8 i/ _
2. **松弛操作**:
/ Q3 I% f8 K$ b   - 对所有边进行松弛操作,循环(V-1)次(V为节点数),尝试更新每个边的距离。& g' p3 k! C6 w, T) l

# i; A; D8 y" k0 h3. **检查负权回路**:
) B$ M4 H, ~9 W% @, N+ O   - 再次对所有边进行一次松弛检查,如果有边的距离仍能被更新,则说明图中存在负权回路。! f9 M# ]/ k5 e% G1 b0 `

: h) r' m7 A5 u; m7 b% M### 示例代码
0 R. Q. I7 W* }
1 @' I6 B! H. Q4 E% o```python% I" g* N, |/ B3 t  N4 S
def bellman_ford(graph, start):
: K' s3 L! @# L9 {6 t- l    # 初始化距离
. I+ S9 M4 O6 A    distances = {node: float('inf') for node in graph}. z8 N/ x$ M3 V7 {: K9 o
    distances[start] = 0
' }" u, a1 f3 E! j& }5 p6 S+ m2 z8 A$ ~/ d/ c5 z
    # 松弛操作
8 Z/ t, z  F& m2 ?& t    for _ in range(len(graph) - 1):
& Z% }1 ~0 U' R6 A0 t$ q        for u in graph:
* s: B" ~* |" g/ c9 f            for v, weight in graph[u].items():
7 E& _& o& h2 T% q) u                if distances[u] + weight < distances[v]:
3 N/ e2 W; l5 M3 ?( s                    distances[v] = distances[u] + weight; T- `% f9 Z$ B3 t2 @) V! `9 [
- g6 L# f! D( T# m, A
    # 检查负权回路8 d  ]. E2 j+ h; Z1 k! e, i) m
    for u in graph:
5 j1 G1 z$ ], ~+ }5 O, h        for v, weight in graph[u].items():! V9 f2 W' w" ~
            if distances[u] + weight < distances[v]:* P. A; ^0 x8 G- A' C: w
                raise ValueError("Graph contains a negative weight cycle")
. @2 ]" @( i0 y; Z. k& L( e# b
7 g5 @% n( q" L7 [9 B2 i7 K    return distances
8 v3 J( c. ^  H' q( O( p+ W+ _  z5 i& A" v
# 示例图(带有负权边)
6 p% u  }+ y0 h; U% _; w( A6 wgraph_with_negative_weight = {
8 B& s. X9 g% ]5 a' y& w, e    'A': {'B': 1, 'C': 4},( c3 W4 ^+ V8 X2 i- S" K/ q- V$ b+ f
    'B': {'C': -3, 'D': 2},
8 Z5 d) N$ B6 o: f    'C': {},3 {3 r6 @7 ~# T- s2 Z
    'D': {'A': -1}5 c/ U; t, C3 C- U1 @% M$ G% p
}
- E1 ^  c$ m6 ?  w! U4 T' e
1 H5 b( E7 A0 M5 [. wstart_node = 'A'
% P0 Q6 w0 C+ s5 q% Nshortest_paths = bellman_ford(graph_with_negative_weight, start_node)
. y1 z: q2 F8 ~! t" |. w& g; Gprint(shortest_paths)  # {'A': 0, 'B': 1, 'C': -2, 'D': 3}" m+ ^% _* H# O; }: ?9 q& Q6 r* B; T
```; I) t3 a  y+ I! [

  e# r% t9 R" r+ h## 3. Floyd-Warshall 算法
% C. z& ]) C# T% K+ v
; H+ [3 F* a, G. l  BFloyd-Warshall 算法用于寻找图中所有节点之间的最短路径,适用于边权任意的图,包括有负权边但无负权回路的情况。" F* X) Q5 x# Z5 B7 u- P

, q& @* S8 U' H& S  T! x### 原理步骤
3 z' B3 r$ Q0 R% o& Q
- Z" D# l& O! k: q1. **初始化**:
- \0 |5 q, A# m: ?- \1 `   - 创建一个距离矩阵,初始化为无穷大,源节点到自身的距离置为0,直接相连的节点的距离为边权。
; }& l3 x" h4 f: O/ U* L$ H
% P2 j( D% \( e! `" s4 z1 D9 X2. **更新路径**:7 `& k+ M: W3 p; R+ F
   - 三重循环遍历所有节点,以每个中间节点尝试更新路径。
+ n: f" O$ g* [/ u  N% W. w9 t2 l3 b& c' `
3. **输出结果**:3 H* X' H) K! v* ]. ]7 Y
   - 最终得到的矩阵即为每对节点之间的最短路径长度。
. U; r+ O) v$ e$ m7 Y" R) v" @1 P+ e8 i
### 示例代码1 v$ ]! o* R+ v% z1 E) w

; Y: \$ m, D6 r% v" ~; n. D```python
& T3 s. l. _; V0 c, Ndef floyd_warshall(graph):3 H1 T4 `; R8 x- T
    # 初始化距离矩阵
' L" \3 m2 `9 \    nodes = list(graph.keys())
  y: `2 d! r/ P7 K) n0 R    distances = {node: {n: float('inf') for n in nodes} for node in nodes}
. s6 R0 S3 }' B: L6 U% e, ]9 b2 [# ?, v8 G1 [8 o5 K* `" d
    for u in nodes:
7 O- l9 A$ m1 O# g2 X4 e3 \        distances[u][u] = 0
# \9 v6 k, c) u. Q: X! L* {        for v, weight in graph[u].items():
; j# p2 e3 T, C' D8 p& P            distances[u][v] = weight
4 h7 e' ^: g' i7 R/ `& s2 c
- w" u  G# M/ c    # 更新路径# f( ]! `8 j& f6 H& @
    for k in nodes:
9 z- u) F4 f6 U) Z" Y        for i in nodes:) }' B" J( r5 O/ }3 V
            for j in nodes:9 n0 c' [/ _1 w( [. m) y% s$ I
                if distances[i][j] > distances[i][k] + distances[k][j]:3 q9 n/ w9 `$ n% `, w/ e: h
                    distances[i][j] = distances[i][k] + distances[k][j]
; e3 ^1 r* Z- Z' n0 E" n" O9 P
$ f4 g" N6 Z. Q% e    return distances/ G% [. S% [( E) n% W

+ G' S; ^0 b) E* H  a# 示例图(可以含负权边)
/ R7 ]% \6 Y* X- Q5 Xgraph_for_floyd = {
2 }% n! z$ b- ~9 C+ a    'A': {'B': 3, 'C': 8, 'D': -4},% b# h. S( x' w6 P& ^: p$ [9 n
    'B': {'C': 1, 'D': 7},
% f1 d# ~- T! m: q    'C': {'B': 4},7 _/ ~; M, M$ O. M( _9 E
    'D': {'A': 2, 'C': -5}
- a, h8 F7 `0 ?}
2 {8 H. l' T( h  G' I. ]; i4 M7 r4 v5 n! P* R
shortest_paths_matrix = floyd_warshall(graph_for_floyd)2 S; k+ G# B3 d; n  y7 A8 b; n
for row in shortest_paths_matrix.items():
/ a  _  `- \/ v, m, p1 s$ x    print(row)
) Z) a/ o! X' E& m8 y  ````1 [. }/ J2 c) {& j

. ?2 ?# J( `) ?7 Q6 q5 c4 V## 总结
* R+ I/ b9 ?2 x- p  B
; @/ A! c/ T5 s" N! W: c- **Dijkstra 算法**适合用于无负权边图,时间复杂度为 \(O((V + E) \log V)\),其中 \(V\) 为节点数,\(E\) 为边数。
+ L- ]2 V! a0 c0 C3 t" M- **Bellman-Ford 算法**适合处理含负权边的图,时间复杂度为 \(O(VE)\)。8 ~, P( S( b2 J' ?- k0 h
- **Floyd-Warshall 算法**用于所有节点对最短路径计算,时间复杂度为 \(O(V^3)\)。
: }7 W1 t0 J6 K# m; w1 \4 Q7 I9 b$ E( S8 f0 r" V9 q
不同算法适用于不同的问题场景,选择适合的算法是解决最短路径问题的关键。如果需要更详细的介绍或有特定问题,欢迎进一步询问!$ V9 l( g% E: h' q5 N# g7 N" a
' ^9 C# R: J" q) q. p

) q1 P% F% @! Z5 ]
% X: ]( x+ G; p' Z' n4 \* N

最短路径算法Python代码.docx

13.29 KB, 下载次数: 0, 下载积分: 体力 -2 点

售价: 2 点体力  [记录]  [购买]






欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5