Skip to content

Commit b0837d3

Browse files
R055Acclausspre-commit-ci[bot]
authoredOct 27, 2023
Increase code coverage for dijkstra algorithm (TheAlgorithms#10695)
* Increase code coverage for dijkstra algorithm * Add missing code coverage Refactor to pass mypy * Fix missing code coverage * Remove code changes, keep doctest * Remove ALL of the code changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dijkstra_algorithm.py --------- Co-authored-by: Christian Clauss <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 185a355 commit b0837d3

File tree

1 file changed

+299
-14
lines changed

1 file changed

+299
-14
lines changed
 

‎graphs/dijkstra_algorithm.py

Lines changed: 299 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,127 @@
1111
class PriorityQueue:
1212
# Based on Min Heap
1313
def __init__(self):
14+
"""
15+
Priority queue class constructor method.
16+
17+
Examples:
18+
>>> priority_queue_test = PriorityQueue()
19+
>>> priority_queue_test.cur_size
20+
0
21+
>>> priority_queue_test.array
22+
[]
23+
>>> priority_queue_test.pos
24+
{}
25+
"""
1426
self.cur_size = 0
1527
self.array = []
1628
self.pos = {} # To store the pos of node in array
1729

1830
def is_empty(self):
31+
"""
32+
Conditional boolean method to determine if the priority queue is empty or not.
33+
34+
Examples:
35+
>>> priority_queue_test = PriorityQueue()
36+
>>> priority_queue_test.is_empty()
37+
True
38+
>>> priority_queue_test.insert((2, 'A'))
39+
>>> priority_queue_test.is_empty()
40+
False
41+
"""
1942
return self.cur_size == 0
2043

2144
def min_heapify(self, idx):
45+
"""
46+
Sorts the queue array so that the minimum element is root.
47+
48+
Examples:
49+
>>> priority_queue_test = PriorityQueue()
50+
>>> priority_queue_test.cur_size = 3
51+
>>> priority_queue_test.pos = {'A': 0, 'B': 1, 'C': 2}
52+
53+
>>> priority_queue_test.array = [(5, 'A'), (10, 'B'), (15, 'C')]
54+
>>> priority_queue_test.min_heapify(0)
55+
Traceback (most recent call last):
56+
...
57+
TypeError: 'list' object is not callable
58+
>>> priority_queue_test.array
59+
[(5, 'A'), (10, 'B'), (15, 'C')]
60+
61+
>>> priority_queue_test.array = [(10, 'A'), (5, 'B'), (15, 'C')]
62+
>>> priority_queue_test.min_heapify(0)
63+
Traceback (most recent call last):
64+
...
65+
TypeError: 'list' object is not callable
66+
>>> priority_queue_test.array
67+
[(10, 'A'), (5, 'B'), (15, 'C')]
68+
69+
>>> priority_queue_test.array = [(10, 'A'), (15, 'B'), (5, 'C')]
70+
>>> priority_queue_test.min_heapify(0)
71+
Traceback (most recent call last):
72+
...
73+
TypeError: 'list' object is not callable
74+
>>> priority_queue_test.array
75+
[(10, 'A'), (15, 'B'), (5, 'C')]
76+
77+
>>> priority_queue_test.array = [(10, 'A'), (5, 'B')]
78+
>>> priority_queue_test.cur_size = len(priority_queue_test.array)
79+
>>> priority_queue_test.pos = {'A': 0, 'B': 1}
80+
>>> priority_queue_test.min_heapify(0)
81+
Traceback (most recent call last):
82+
...
83+
TypeError: 'list' object is not callable
84+
>>> priority_queue_test.array
85+
[(10, 'A'), (5, 'B')]
86+
"""
2287
lc = self.left(idx)
2388
rc = self.right(idx)
24-
if lc < self.cur_size and self.array(lc)[0] < self.array(idx)[0]:
89+
if lc < self.cur_size and self.array(lc)[0] < self.array[idx][0]:
2590
smallest = lc
2691
else:
2792
smallest = idx
28-
if rc < self.cur_size and self.array(rc)[0] < self.array(smallest)[0]:
93+
if rc < self.cur_size and self.array(rc)[0] < self.array[smallest][0]:
2994
smallest = rc
3095
if smallest != idx:
3196
self.swap(idx, smallest)
3297
self.min_heapify(smallest)
3398

3499
def insert(self, tup):
35-
# Inserts a node into the Priority Queue
100+
"""
101+
Inserts a node into the Priority Queue.
102+
103+
Examples:
104+
>>> priority_queue_test = PriorityQueue()
105+
>>> priority_queue_test.insert((10, 'A'))
106+
>>> priority_queue_test.array
107+
[(10, 'A')]
108+
>>> priority_queue_test.insert((15, 'B'))
109+
>>> priority_queue_test.array
110+
[(10, 'A'), (15, 'B')]
111+
>>> priority_queue_test.insert((5, 'C'))
112+
>>> priority_queue_test.array
113+
[(5, 'C'), (10, 'A'), (15, 'B')]
114+
"""
36115
self.pos[tup[1]] = self.cur_size
37116
self.cur_size += 1
38117
self.array.append((sys.maxsize, tup[1]))
39118
self.decrease_key((sys.maxsize, tup[1]), tup[0])
40119

41120
def extract_min(self):
42-
# Removes and returns the min element at top of priority queue
121+
"""
122+
Removes and returns the min element at top of priority queue.
123+
124+
Examples:
125+
>>> priority_queue_test = PriorityQueue()
126+
>>> priority_queue_test.array = [(10, 'A'), (15, 'B')]
127+
>>> priority_queue_test.cur_size = len(priority_queue_test.array)
128+
>>> priority_queue_test.pos = {'A': 0, 'B': 1}
129+
>>> priority_queue_test.insert((5, 'C'))
130+
>>> priority_queue_test.extract_min()
131+
'C'
132+
>>> priority_queue_test.array[0]
133+
(15, 'B')
134+
"""
43135
min_node = self.array[0][1]
44136
self.array[0] = self.array[self.cur_size - 1]
45137
self.cur_size -= 1
@@ -48,27 +140,80 @@ def extract_min(self):
48140
return min_node
49141

50142
def left(self, i):
51-
# returns the index of left child
143+
"""
144+
Returns the index of left child
145+
146+
Examples:
147+
>>> priority_queue_test = PriorityQueue()
148+
>>> priority_queue_test.left(0)
149+
1
150+
>>> priority_queue_test.left(1)
151+
3
152+
"""
52153
return 2 * i + 1
53154

54155
def right(self, i):
55-
# returns the index of right child
156+
"""
157+
Returns the index of right child
158+
159+
Examples:
160+
>>> priority_queue_test = PriorityQueue()
161+
>>> priority_queue_test.right(0)
162+
2
163+
>>> priority_queue_test.right(1)
164+
4
165+
"""
56166
return 2 * i + 2
57167

58168
def par(self, i):
59-
# returns the index of parent
169+
"""
170+
Returns the index of parent
171+
172+
Examples:
173+
>>> priority_queue_test = PriorityQueue()
174+
>>> priority_queue_test.par(1)
175+
0
176+
>>> priority_queue_test.par(2)
177+
1
178+
>>> priority_queue_test.par(4)
179+
2
180+
"""
60181
return math.floor(i / 2)
61182

62183
def swap(self, i, j):
63-
# swaps array elements at indices i and j
64-
# update the pos{}
184+
"""
185+
Swaps array elements at indices i and j, update the pos{}
186+
187+
Examples:
188+
>>> priority_queue_test = PriorityQueue()
189+
>>> priority_queue_test.array = [(10, 'A'), (15, 'B')]
190+
>>> priority_queue_test.cur_size = len(priority_queue_test.array)
191+
>>> priority_queue_test.pos = {'A': 0, 'B': 1}
192+
>>> priority_queue_test.swap(0, 1)
193+
>>> priority_queue_test.array
194+
[(15, 'B'), (10, 'A')]
195+
>>> priority_queue_test.pos
196+
{'A': 1, 'B': 0}
197+
"""
65198
self.pos[self.array[i][1]] = j
66199
self.pos[self.array[j][1]] = i
67200
temp = self.array[i]
68201
self.array[i] = self.array[j]
69202
self.array[j] = temp
70203

71204
def decrease_key(self, tup, new_d):
205+
"""
206+
Decrease the key value for a given tuple, assuming the new_d is at most old_d.
207+
208+
Examples:
209+
>>> priority_queue_test = PriorityQueue()
210+
>>> priority_queue_test.array = [(10, 'A'), (15, 'B')]
211+
>>> priority_queue_test.cur_size = len(priority_queue_test.array)
212+
>>> priority_queue_test.pos = {'A': 0, 'B': 1}
213+
>>> priority_queue_test.decrease_key((10, 'A'), 5)
214+
>>> priority_queue_test.array
215+
[(5, 'A'), (15, 'B')]
216+
"""
72217
idx = self.pos[tup[1]]
73218
# assuming the new_d is atmost old_d
74219
self.array[idx] = (new_d, tup[1])
@@ -79,15 +224,37 @@ def decrease_key(self, tup, new_d):
79224

80225
class Graph:
81226
def __init__(self, num):
227+
"""
228+
Graph class constructor
229+
230+
Examples:
231+
>>> graph_test = Graph(1)
232+
>>> graph_test.num_nodes
233+
1
234+
>>> graph_test.dist
235+
[0]
236+
>>> graph_test.par
237+
[-1]
238+
>>> graph_test.adjList
239+
{}
240+
"""
82241
self.adjList = {} # To store graph: u -> (v,w)
83242
self.num_nodes = num # Number of nodes in graph
84243
# To store the distance from source vertex
85244
self.dist = [0] * self.num_nodes
86245
self.par = [-1] * self.num_nodes # To store the path
87246

88247
def add_edge(self, u, v, w):
89-
# Edge going from node u to v and v to u with weight w
90-
# u (w)-> v, v (w) -> u
248+
"""
249+
Add edge going from node u to v and v to u with weight w: u (w)-> v, v (w) -> u
250+
251+
Examples:
252+
>>> graph_test = Graph(1)
253+
>>> graph_test.add_edge(1, 2, 1)
254+
>>> graph_test.add_edge(2, 3, 2)
255+
>>> graph_test.adjList
256+
{1: [(2, 1)], 2: [(1, 1), (3, 2)], 3: [(2, 2)]}
257+
"""
91258
# Check if u already in graph
92259
if u in self.adjList:
93260
self.adjList[u].append((v, w))
@@ -101,11 +268,99 @@ def add_edge(self, u, v, w):
101268
self.adjList[v] = [(u, w)]
102269

103270
def show_graph(self):
104-
# u -> v(w)
271+
"""
272+
Show the graph: u -> v(w)
273+
274+
Examples:
275+
>>> graph_test = Graph(1)
276+
>>> graph_test.add_edge(1, 2, 1)
277+
>>> graph_test.show_graph()
278+
1 -> 2(1)
279+
2 -> 1(1)
280+
>>> graph_test.add_edge(2, 3, 2)
281+
>>> graph_test.show_graph()
282+
1 -> 2(1)
283+
2 -> 1(1) -> 3(2)
284+
3 -> 2(2)
285+
"""
105286
for u in self.adjList:
106287
print(u, "->", " -> ".join(str(f"{v}({w})") for v, w in self.adjList[u]))
107288

108289
def dijkstra(self, src):
290+
"""
291+
Dijkstra algorithm
292+
293+
Examples:
294+
>>> graph_test = Graph(3)
295+
>>> graph_test.add_edge(0, 1, 2)
296+
>>> graph_test.add_edge(1, 2, 2)
297+
>>> graph_test.dijkstra(0)
298+
Distance from node: 0
299+
Node 0 has distance: 0
300+
Node 1 has distance: 2
301+
Node 2 has distance: 4
302+
>>> graph_test.dist
303+
[0, 2, 4]
304+
305+
>>> graph_test = Graph(2)
306+
>>> graph_test.add_edge(0, 1, 2)
307+
>>> graph_test.dijkstra(0)
308+
Distance from node: 0
309+
Node 0 has distance: 0
310+
Node 1 has distance: 2
311+
>>> graph_test.dist
312+
[0, 2]
313+
314+
>>> graph_test = Graph(3)
315+
>>> graph_test.add_edge(0, 1, 2)
316+
>>> graph_test.dijkstra(0)
317+
Distance from node: 0
318+
Node 0 has distance: 0
319+
Node 1 has distance: 2
320+
Node 2 has distance: 0
321+
>>> graph_test.dist
322+
[0, 2, 0]
323+
324+
>>> graph_test = Graph(3)
325+
>>> graph_test.add_edge(0, 1, 2)
326+
>>> graph_test.add_edge(1, 2, 2)
327+
>>> graph_test.add_edge(0, 2, 1)
328+
>>> graph_test.dijkstra(0)
329+
Distance from node: 0
330+
Node 0 has distance: 0
331+
Node 1 has distance: 2
332+
Node 2 has distance: 1
333+
>>> graph_test.dist
334+
[0, 2, 1]
335+
336+
>>> graph_test = Graph(4)
337+
>>> graph_test.add_edge(0, 1, 4)
338+
>>> graph_test.add_edge(1, 2, 2)
339+
>>> graph_test.add_edge(2, 3, 1)
340+
>>> graph_test.add_edge(0, 2, 3)
341+
>>> graph_test.dijkstra(0)
342+
Distance from node: 0
343+
Node 0 has distance: 0
344+
Node 1 has distance: 4
345+
Node 2 has distance: 3
346+
Node 3 has distance: 4
347+
>>> graph_test.dist
348+
[0, 4, 3, 4]
349+
350+
>>> graph_test = Graph(4)
351+
>>> graph_test.add_edge(0, 1, 4)
352+
>>> graph_test.add_edge(1, 2, 2)
353+
>>> graph_test.add_edge(2, 3, 1)
354+
>>> graph_test.add_edge(0, 2, 7)
355+
>>> graph_test.dijkstra(0)
356+
Distance from node: 0
357+
Node 0 has distance: 0
358+
Node 1 has distance: 4
359+
Node 2 has distance: 6
360+
Node 3 has distance: 7
361+
>>> graph_test.dist
362+
[0, 4, 6, 7]
363+
"""
109364
# Flush old junk values in par[]
110365
self.par = [-1] * self.num_nodes
111366
# src is the source node
@@ -135,13 +390,40 @@ def dijkstra(self, src):
135390
self.show_distances(src)
136391

137392
def show_distances(self, src):
393+
"""
394+
Show the distances from src to all other nodes in a graph
395+
396+
Examples:
397+
>>> graph_test = Graph(1)
398+
>>> graph_test.show_distances(0)
399+
Distance from node: 0
400+
Node 0 has distance: 0
401+
"""
138402
print(f"Distance from node: {src}")
139403
for u in range(self.num_nodes):
140404
print(f"Node {u} has distance: {self.dist[u]}")
141405

142406
def show_path(self, src, dest):
143-
# To show the shortest path from src to dest
144-
# WARNING: Use it *after* calling dijkstra
407+
"""
408+
Shows the shortest path from src to dest.
409+
WARNING: Use it *after* calling dijkstra.
410+
411+
Examples:
412+
>>> graph_test = Graph(4)
413+
>>> graph_test.add_edge(0, 1, 1)
414+
>>> graph_test.add_edge(1, 2, 2)
415+
>>> graph_test.add_edge(2, 3, 3)
416+
>>> graph_test.dijkstra(0)
417+
Distance from node: 0
418+
Node 0 has distance: 0
419+
Node 1 has distance: 1
420+
Node 2 has distance: 3
421+
Node 3 has distance: 6
422+
>>> graph_test.show_path(0, 3) # doctest: +NORMALIZE_WHITESPACE
423+
----Path to reach 3 from 0----
424+
0 -> 1 -> 2 -> 3
425+
Total cost of path: 6
426+
"""
145427
path = []
146428
cost = 0
147429
temp = dest
@@ -167,6 +449,9 @@ def show_path(self, src, dest):
167449

168450

169451
if __name__ == "__main__":
452+
from doctest import testmod
453+
454+
testmod()
170455
graph = Graph(9)
171456
graph.add_edge(0, 1, 4)
172457
graph.add_edge(0, 7, 8)

0 commit comments

Comments
 (0)
Please sign in to comment.