Skip to content

Commit 945d25f

Browse files
committed
change KeyObject.key type to float (to be able to safely assign math.inf which is float), add asserts to check if not None, many improvements to type hinting to make mypy happy
1 parent 917d177 commit 945d25f

File tree

12 files changed

+108
-90
lines changed

12 files changed

+108
-90
lines changed

src/book/chapter6/problem1.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from book.chapter6.section5 import max_heap_insert
22
from book.data_structures import Heap
33
from book.data_structures import KeyObject
4+
from book.data_structures import T
45
from util import range_of
56

67

7-
def build_max_heap_(A: Heap[KeyObject], n: int) -> None:
8+
def build_max_heap_(A: Heap[KeyObject[T]], n: int) -> None:
89
"""Converts an array into a max-heap by repeatedly inserting elements to the heap.
910
1011
Implements:

src/book/chapter6/section5.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
from book.chapter6.section2 import max_heapify
55
from book.data_structures import KeyObject
66
from book.data_structures import PriorityQueue
7+
from book.data_structures import T
78
from solutions.chapter2.section1.exercise4 import linear_search
89

910

10-
def max_heap_maximum(A: PriorityQueue) -> KeyObject:
11+
def max_heap_maximum(A: PriorityQueue[T]) -> KeyObject[T]:
1112
"""Returns the element of the dynamic set with the largest key.
1213
1314
Implements:
@@ -24,7 +25,7 @@ def max_heap_maximum(A: PriorityQueue) -> KeyObject:
2425
return A[1]
2526

2627

27-
def max_heap_extract_max(A: PriorityQueue) -> KeyObject:
28+
def max_heap_extract_max(A: PriorityQueue[T]) -> KeyObject[T]:
2829
"""Removes and returns the element of the dynamic set with the largest key.
2930
3031
Implements:
@@ -43,7 +44,7 @@ def max_heap_extract_max(A: PriorityQueue) -> KeyObject:
4344
return max
4445

4546

46-
def max_heap_increase_key(A: PriorityQueue, x: KeyObject, k: int) -> None:
47+
def max_heap_increase_key(A: PriorityQueue[T], x: KeyObject[T], k: float) -> None:
4748
"""Increases the value of the element's key to the new value.
4849
4950
Implements:
@@ -63,11 +64,13 @@ def max_heap_increase_key(A: PriorityQueue, x: KeyObject, k: int) -> None:
6364
i = parent(i)
6465

6566

66-
def __find_index_of_object(A: PriorityQueue, x: KeyObject) -> int:
67-
return linear_search(A, A.heap_size, x)
67+
def __find_index_of_object(A: PriorityQueue[T], x: KeyObject[T]) -> int:
68+
index = linear_search(A, A.heap_size, x)
69+
assert index is not None
70+
return index
6871

6972

70-
def max_heap_insert(A: PriorityQueue, x: KeyObject, n: int) -> None:
73+
def max_heap_insert(A: PriorityQueue[T], x: KeyObject[T], n: int) -> None:
7174
"""Inserts a new element into the dynamic set.
7275
7376
Implements:

src/book/data_structures.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33
from builtins import len
44
from collections.abc import Callable
55
from typing import Any
6-
from typing import Dict
76
from typing import Generic
87
from typing import Literal
98
from typing import TypeVar
109

1110
from typing_extensions import Protocol
12-
13-
from util import range_of
11+
from typing_extensions import TypeAlias
1412

1513
T = TypeVar('T')
1614
CT = TypeVar('CT', bound='Comparable')
@@ -35,7 +33,7 @@ class Indexable(Protocol[T]):
3533
__setitem__: Callable[[int, T], None]
3634

3735

38-
Bit = Literal[0, 1]
36+
Bit: TypeAlias = Literal[0, 1]
3937

4038

4139
class Array(Generic[T]):
@@ -140,35 +138,35 @@ class Heap(Array[CT]):
140138
heap_size: int = 0
141139

142140

143-
class KeyObject:
144-
def __init__(self, key: int, data=None) -> None:
141+
class KeyObject(Generic[T]):
142+
def __init__(self, key: float, data: T) -> None:
145143
self.key = key
146144
self.data = data
147145

148-
def __lt__(self, other: KeyObject) -> bool:
146+
def __lt__(self, other: KeyObject[T]) -> bool:
149147
return self.key < other.key
150148

151-
def __le__(self, other: KeyObject) -> bool:
149+
def __le__(self, other: KeyObject[T]) -> bool:
152150
return self.key <= other.key
153151

154-
def __gt__(self, other: KeyObject) -> bool:
152+
def __gt__(self, other: KeyObject[T]) -> bool:
155153
return self.key > other.key
156154

157-
def __ge__(self, other: KeyObject) -> bool:
155+
def __ge__(self, other: KeyObject[T]) -> bool:
158156
return self.key >= other.key
159157

160158

161159
# TODO(#476) Consider removing this data structure, and instead implement the priority queue directly on a heap.
162160
# Clarify if we need the mapping between objects and heap indices, because as mentioned in the book, a heap
163161
# implementing a priority queue contains pointers to the objects, which should suffice to address the objects.
164-
class PriorityQueue(Heap[KeyObject]):
162+
class PriorityQueue(Heap[KeyObject[T]]):
165163

166164
def __init__(self, n: int) -> None:
167165
# TODO(#21) implement the object mappings with a hash table instead of Python dictionary
168166
super().__init__(1, n)
169167
self.heap_size: int = 0
170-
self.mapping: Dict[KeyObject, int] = {}
168+
self.mapping: dict[KeyObject[T], int] = {}
171169

172-
def __setitem__(self, index: int, value: KeyObject) -> None:
170+
def __setitem__(self, index: int, value: KeyObject[T]) -> None:
173171
super().__setitem__(index, value)
174172
self.mapping[value] = index

src/solutions/chapter6/section5/exercise10.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
from book.chapter6.section5 import max_heap_increase_key
55
from book.data_structures import KeyObject
66
from book.data_structures import PriorityQueue
7+
from book.data_structures import T
78

89

9-
def max_heap_delete(A: PriorityQueue, x: KeyObject) -> None:
10+
def max_heap_delete(A: PriorityQueue[T], x: KeyObject[T]) -> None:
1011
"""Deletes an element from a max-heap.
1112
1213
Implements:
Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
from __future__ import annotations
22

3-
from typing import Generic
3+
from typing_extensions import TypeAlias
44

55
from book.data_structures import Array
66
from book.data_structures import KeyObject
77
from book.data_structures import PriorityQueue
8-
from book.data_structures import T
98
from solutions.chapter6.section5.exercise3 import min_heap_extract_min
109
from solutions.chapter6.section5.exercise3 import min_heap_insert
1110
from util import range_of
1211

1312

1413
# TODO(#20) replace by the general list node and list classes once they will have been added
15-
class SortedListNode(Generic[T]):
16-
key: T
17-
next: SortedListNode[T] | None = None
14+
class SortedListNode:
15+
key: float
16+
next: SortedListNode | None = None
1817

1918

20-
class SortedList(Generic[T]):
21-
head: SortedListNode[T] | None = None
19+
class SortedList:
20+
head: SortedListNode | None = None
2221

2322

24-
def merge_sorted_lists(sorted_lists: Array[SortedList[T]], k: int) -> SortedList[T]:
23+
NodePayloadType: TypeAlias = tuple[SortedListNode, SortedList]
24+
25+
26+
def merge_sorted_lists(sorted_lists: Array[SortedList], k: int) -> SortedList:
2527
"""Merges sorted lists into one sorted list.
2628
2729
Args:
@@ -31,24 +33,26 @@ def merge_sorted_lists(sorted_lists: Array[SortedList[T]], k: int) -> SortedList
3133
Returns:
3234
The sorted list containing all elements from the input sorted lists.
3335
"""
34-
Q = PriorityQueue(k)
36+
Q = PriorityQueue[NodePayloadType](k)
3537
for i in range_of(1, to=k):
3638
sorted_list = sorted_lists[i]
3739
if sorted_list.head is not None:
3840
x = sorted_list.head
3941
sorted_list.head = sorted_list.head.next
4042
x.next = None
41-
min_heap_insert(Q, KeyObject(key=x.key, data=(x, sorted_list)), k)
42-
merged_list = SortedList[T]()
43-
tail = None
43+
min_heap_insert(Q, KeyObject[NodePayloadType](key=x.key, data=(x, sorted_list)), k)
44+
merged_list = SortedList()
45+
tail: SortedListNode | None = None
4446
while Q.heap_size > 0:
45-
element, list_ = min_heap_extract_min(Q).data
47+
head_object = min_heap_extract_min(Q)
48+
element, list_ = head_object.data
4649
if merged_list.head is None:
4750
tail = merged_list.head = element
4851
else:
52+
assert tail is not None
4953
tail.next = element
5054
tail = tail.next
5155
if list_.head is not None:
52-
min_heap_insert(Q, KeyObject(key=list_.head.key, data=(list_.head, list_)), k)
56+
min_heap_insert(Q, KeyObject[NodePayloadType](key=list_.head.key, data=(list_.head, list_)), k)
5357
list_.head = list_.head.next
5458
return merged_list

src/solutions/chapter6/section5/exercise3.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
from book.chapter6.section1 import parent
44
from book.data_structures import KeyObject
55
from book.data_structures import PriorityQueue
6+
from book.data_structures import T
67
from solutions.chapter2.section1.exercise4 import linear_search
78
from solutions.chapter6.section2.exercise3 import min_heapify
89

910

10-
def min_heap_minimum(A: PriorityQueue) -> KeyObject:
11+
def min_heap_minimum(A: PriorityQueue[T]) -> KeyObject[T]:
1112
"""Returns the element of the dynamic set with the smallest key.
1213
1314
Implements:
@@ -24,7 +25,7 @@ def min_heap_minimum(A: PriorityQueue) -> KeyObject:
2425
return A[1]
2526

2627

27-
def min_heap_extract_min(A: PriorityQueue) -> KeyObject:
28+
def min_heap_extract_min(A: PriorityQueue[T]) -> KeyObject[T]:
2829
"""Removes and returns the element of the dynamic set with the smallest key.
2930
3031
Implements:
@@ -43,7 +44,7 @@ def min_heap_extract_min(A: PriorityQueue) -> KeyObject:
4344
return min
4445

4546

46-
def min_heap_decrease_key(A: PriorityQueue, x: KeyObject, k: int) -> None:
47+
def min_heap_decrease_key(A: PriorityQueue[T], x: KeyObject[T], k: float) -> None:
4748
"""Decreases the value of the element's key to the new value.
4849
4950
Implements:
@@ -63,11 +64,13 @@ def min_heap_decrease_key(A: PriorityQueue, x: KeyObject, k: int) -> None:
6364
i = parent(i)
6465

6566

66-
def __find_index_of_object(A: PriorityQueue, x: KeyObject) -> int:
67-
return linear_search(A, A.heap_size, x)
67+
def __find_index_of_object(A: PriorityQueue[T], x: KeyObject[T]) -> int:
68+
index = linear_search(A, A.heap_size, x)
69+
assert index is not None
70+
return index
6871

6972

70-
def min_heap_insert(A: PriorityQueue, x: KeyObject, n: int) -> None:
73+
def min_heap_insert(A: PriorityQueue[T], x: KeyObject[T], n: int) -> None:
7174
"""Inserts a new element into the dynamic set.
7275
7376
Implements:

src/solutions/chapter6/section5/exercise4.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from book.chapter6.section2 import max_heapify
22
from book.data_structures import KeyObject
33
from book.data_structures import PriorityQueue
4+
from book.data_structures import T
45
from solutions.chapter2.section1.exercise4 import linear_search
56

67

7-
def max_heap_decrease_key(A: PriorityQueue, x: KeyObject, k: int) -> None:
8+
def max_heap_decrease_key(A: PriorityQueue[T], x: KeyObject[T], k: float) -> None:
89
"""Increases the value of the element's key to the new value.
910
1011
Implements:
@@ -22,5 +23,7 @@ def max_heap_decrease_key(A: PriorityQueue, x: KeyObject, k: int) -> None:
2223
max_heapify(A, i)
2324

2425

25-
def __find_index_of_object(A: PriorityQueue, x: KeyObject) -> int:
26-
return linear_search(A, A.heap_size, x)
26+
def __find_index_of_object(A: PriorityQueue[T], x: KeyObject[T]) -> int:
27+
index = linear_search(A, A.heap_size, x)
28+
assert index is not None
29+
return index

src/solutions/chapter6/section5/exercise8.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from book.chapter6.section1 import parent
22
from book.data_structures import KeyObject
33
from book.data_structures import PriorityQueue
4+
from book.data_structures import T
45
from solutions.chapter2.section1.exercise4 import linear_search
56

67

7-
def max_heap_increase_key_(A: PriorityQueue, x: KeyObject, k: int) -> None:
8+
def max_heap_increase_key_(A: PriorityQueue[T], x: KeyObject[T], k: float) -> None:
89
"""Increases the value of the element's key to the new value. This version of the procedure uses fewer object
910
assignments than the original Max-Heap-Insert-Key.
1011
@@ -26,5 +27,7 @@ def max_heap_increase_key_(A: PriorityQueue, x: KeyObject, k: int) -> None:
2627
A[i] = x
2728

2829

29-
def __find_index_of_object(A: PriorityQueue, x: KeyObject) -> int:
30-
return linear_search(A, A.heap_size, x)
30+
def __find_index_of_object(A: PriorityQueue[T], x: KeyObject[T]) -> int:
31+
index = linear_search(A, A.heap_size, x)
32+
assert index is not None
33+
return index

src/solutions/chapter6/section5/exercise9.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22
from book.chapter6.section5 import max_heap_insert
33
from book.data_structures import KeyObject
44
from book.data_structures import PriorityQueue
5+
from book.data_structures import T
56
from solutions.chapter6.section5.exercise3 import min_heap_extract_min
67
from solutions.chapter6.section5.exercise3 import min_heap_insert
78

89

9-
class ControlledPriorityQueue(PriorityQueue):
10+
class ControlledPriorityQueue(PriorityQueue[T]):
1011
priority: int = 1
1112

1213

13-
def min_heap_enqueue(A: ControlledPriorityQueue, x: KeyObject, n: int) -> None:
14+
def min_heap_enqueue(A: ControlledPriorityQueue[T], x: KeyObject[T], n: int) -> None:
1415
"""Inserts an element into a first-in, first-out queue implemented with a priority queue.
1516
1617
Implements:
@@ -26,7 +27,7 @@ def min_heap_enqueue(A: ControlledPriorityQueue, x: KeyObject, n: int) -> None:
2627
A.priority += 1
2728

2829

29-
def min_heap_dequeue(A: ControlledPriorityQueue) -> KeyObject:
30+
def min_heap_dequeue(A: ControlledPriorityQueue[T]) -> KeyObject[T]:
3031
"""Deletes an element from a first-in, first-out queue implemented with a priority queue.
3132
3233
Implements:
@@ -41,7 +42,7 @@ def min_heap_dequeue(A: ControlledPriorityQueue) -> KeyObject:
4142
return min_heap_extract_min(A)
4243

4344

44-
def max_heap_push(A: ControlledPriorityQueue, x: KeyObject, n: int) -> None:
45+
def max_heap_push(A: ControlledPriorityQueue[T], x: KeyObject[T], n: int) -> None:
4546
"""Inserts an element into a stack implemented with a priority queue.
4647
4748
Implements:
@@ -57,7 +58,7 @@ def max_heap_push(A: ControlledPriorityQueue, x: KeyObject, n: int) -> None:
5758
A.priority += 1
5859

5960

60-
def max_heap_pop(A: ControlledPriorityQueue) -> KeyObject:
61+
def max_heap_pop(A: ControlledPriorityQueue[T]) -> KeyObject[T]:
6162
"""Deletes an element from a stack implemented with a priority queue.
6263
6364
Implements:

0 commit comments

Comments
 (0)