11
11
class PriorityQueue :
12
12
# Based on Min Heap
13
13
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
+ """
14
26
self .cur_size = 0
15
27
self .array = []
16
28
self .pos = {} # To store the pos of node in array
17
29
18
30
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
+ """
19
42
return self .cur_size == 0
20
43
21
44
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
+ """
22
87
lc = self .left (idx )
23
88
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 ]:
25
90
smallest = lc
26
91
else :
27
92
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 ]:
29
94
smallest = rc
30
95
if smallest != idx :
31
96
self .swap (idx , smallest )
32
97
self .min_heapify (smallest )
33
98
34
99
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
+ """
36
115
self .pos [tup [1 ]] = self .cur_size
37
116
self .cur_size += 1
38
117
self .array .append ((sys .maxsize , tup [1 ]))
39
118
self .decrease_key ((sys .maxsize , tup [1 ]), tup [0 ])
40
119
41
120
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
+ """
43
135
min_node = self .array [0 ][1 ]
44
136
self .array [0 ] = self .array [self .cur_size - 1 ]
45
137
self .cur_size -= 1
@@ -48,27 +140,80 @@ def extract_min(self):
48
140
return min_node
49
141
50
142
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
+ """
52
153
return 2 * i + 1
53
154
54
155
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
+ """
56
166
return 2 * i + 2
57
167
58
168
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
+ """
60
181
return math .floor (i / 2 )
61
182
62
183
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
+ """
65
198
self .pos [self .array [i ][1 ]] = j
66
199
self .pos [self .array [j ][1 ]] = i
67
200
temp = self .array [i ]
68
201
self .array [i ] = self .array [j ]
69
202
self .array [j ] = temp
70
203
71
204
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
+ """
72
217
idx = self .pos [tup [1 ]]
73
218
# assuming the new_d is atmost old_d
74
219
self .array [idx ] = (new_d , tup [1 ])
@@ -79,15 +224,37 @@ def decrease_key(self, tup, new_d):
79
224
80
225
class Graph :
81
226
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
+ """
82
241
self .adjList = {} # To store graph: u -> (v,w)
83
242
self .num_nodes = num # Number of nodes in graph
84
243
# To store the distance from source vertex
85
244
self .dist = [0 ] * self .num_nodes
86
245
self .par = [- 1 ] * self .num_nodes # To store the path
87
246
88
247
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
+ """
91
258
# Check if u already in graph
92
259
if u in self .adjList :
93
260
self .adjList [u ].append ((v , w ))
@@ -101,11 +268,99 @@ def add_edge(self, u, v, w):
101
268
self .adjList [v ] = [(u , w )]
102
269
103
270
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
+ """
105
286
for u in self .adjList :
106
287
print (u , "->" , " -> " .join (str (f"{ v } ({ w } )" ) for v , w in self .adjList [u ]))
107
288
108
289
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
+ """
109
364
# Flush old junk values in par[]
110
365
self .par = [- 1 ] * self .num_nodes
111
366
# src is the source node
@@ -135,13 +390,40 @@ def dijkstra(self, src):
135
390
self .show_distances (src )
136
391
137
392
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
+ """
138
402
print (f"Distance from node: { src } " )
139
403
for u in range (self .num_nodes ):
140
404
print (f"Node { u } has distance: { self .dist [u ]} " )
141
405
142
406
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
+ """
145
427
path = []
146
428
cost = 0
147
429
temp = dest
@@ -167,6 +449,9 @@ def show_path(self, src, dest):
167
449
168
450
169
451
if __name__ == "__main__" :
452
+ from doctest import testmod
453
+
454
+ testmod ()
170
455
graph = Graph (9 )
171
456
graph .add_edge (0 , 1 , 4 )
172
457
graph .add_edge (0 , 7 , 8 )
0 commit comments