Skip to content

Commit 245a527

Browse files
Merge pull request wangzheng0822#226 from KPatr1ck/dijkstra_bug_fix
dijkstra algorithm in Python
2 parents cae28a8 + 22c6bbd commit 245a527

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/usr/bin/python
2+
# -*- coding: UTF-8 -*-
3+
4+
from typing import List, Generator
5+
import heapq
6+
7+
8+
class Graph:
9+
def __init__(self, vertex_count: int) -> None:
10+
self.adj = [[] for _ in range(vertex_count)]
11+
12+
def add_edge(self, s: int, t: int, w: int) -> None:
13+
edge = Edge(s, t, w)
14+
self.adj[s].append(edge)
15+
16+
def __len__(self) -> int:
17+
return len(self.adj)
18+
19+
20+
class Vertex:
21+
def __init__(self, v: int, dist: int) -> None:
22+
self.id = v
23+
self.dist = dist
24+
25+
def __gt__(self, other) -> bool:
26+
return self.dist > other.dist
27+
28+
def __repr__(self) -> str:
29+
return str((self.id, self.dist))
30+
31+
32+
class Edge:
33+
def __init__(self, source: int, target: int, weight: int) -> None:
34+
self.s = source
35+
self.t = target
36+
self.w = weight
37+
38+
39+
class VertexPriorityQueue:
40+
def __init__(self) -> None:
41+
self.vertices = []
42+
43+
def get(self) -> Vertex:
44+
return heapq.heappop(self.vertices)
45+
46+
def put(self, v: Vertex) -> None:
47+
self.vertices.append(v)
48+
self.update_priority()
49+
50+
def empty(self) -> bool:
51+
return len(self.vertices) == 0
52+
53+
def update_priority(self) -> None:
54+
heapq.heapify(self.vertices)
55+
56+
def __repr__(self) -> str:
57+
return str(self.vertices)
58+
59+
60+
def dijkstra(g: Graph, s: int, t: int) -> int:
61+
size = len(g)
62+
63+
pq = VertexPriorityQueue() # 节点队列
64+
in_queue = [False] * size # 已入队标记
65+
vertices = [ # 需要随时更新离s的最短距离的节点列表
66+
Vertex(v, float('inf')) for v in range(size)
67+
]
68+
predecessor = [-1] * size # 先驱
69+
70+
vertices[s].dist = 0
71+
pq.put(vertices[s])
72+
in_queue[s] = True
73+
74+
while not pq.empty():
75+
v = pq.get()
76+
if v.id == t:
77+
break
78+
for edge in g.adj[v.id]:
79+
if v.dist + edge.w < vertices[edge.t].dist:
80+
# 当修改了pq中的元素的优先级后:
81+
# 1. 有入队操作:触发了pq的堆化,此后出队可以取到优先级最高的顶点
82+
# 2. 无入队操作:此后出队取到的顶点可能不是优先级最高的,会有bug
83+
# 为确保正确,需要手动更新一次
84+
vertices[edge.t].dist = v.dist + edge.w
85+
predecessor[edge.t] = v.id
86+
pq.update_priority() # 更新堆结构
87+
if not in_queue[edge.t]:
88+
pq.put(vertices[edge.t])
89+
in_queue[edge.t] = True
90+
91+
for n in print_path(s, t, predecessor):
92+
if n == t:
93+
print(t)
94+
else:
95+
print(n, end=' -> ')
96+
return vertices[t].dist
97+
98+
99+
def print_path(s: int, t: int, p: List[int]) -> Generator[int, None, None]:
100+
if t == s:
101+
yield s
102+
else:
103+
yield from print_path(s, p[t], p)
104+
yield t
105+
106+
107+
if __name__ == '__main__':
108+
g = Graph(6)
109+
g.add_edge(0, 1, 10)
110+
g.add_edge(0, 4, 15)
111+
g.add_edge(1, 2, 15)
112+
g.add_edge(1, 3, 2)
113+
g.add_edge(2, 5, 5)
114+
g.add_edge(3, 2, 1)
115+
g.add_edge(3, 5, 12)
116+
g.add_edge(4, 5, 10)
117+
print(dijkstra(g, 0, 5))
118+
119+
# 下面这个用例可以暴露更新队列元素优先级的问题
120+
# g = Graph(4)
121+
# g.add_edge(0, 1, 18)
122+
# g.add_edge(0, 2, 3)
123+
# g.add_edge(2, 1, 1)
124+
# g.add_edge(1, 3, 5)
125+
# g.add_edge(2, 3, 8)
126+
# g.add_edge(0, 3, 15)
127+
# print(dijkstra(g, 0, 3))

0 commit comments

Comments
 (0)