From 9823e26caa3b90bfe2b352082f2c127fecc1e314 Mon Sep 17 00:00:00 2001 From: Suyashd999 Date: Sat, 28 Oct 2023 23:13:58 +0530 Subject: [PATCH 1/4] Added doctest to heap.py --- data_structures/heap/heap.py | 75 ++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/data_structures/heap/heap.py b/data_structures/heap/heap.py index c1004f349479..29bff3af07e3 100644 --- a/data_structures/heap/heap.py +++ b/data_structures/heap/heap.py @@ -81,6 +81,9 @@ def right_child_idx(self, parent_idx: int) -> int | None: def max_heapify(self, index: int) -> None: """ correct a single violation of the heap property in a subtree's root. + + It is the function that is responsible for restoring the property + of Max heap i.e the maximum element is always at top. """ if index < self.heap_size: violation: int = index @@ -99,7 +102,29 @@ def max_heapify(self, index: int) -> None: self.max_heapify(violation) def build_max_heap(self, collection: Iterable[T]) -> None: - """build max heap from an unsorted array""" + """ + build max heap from an unsorted array + + >>> h = Heap() + >>> h.build_max_heap([20,40,50,20,10]) + >>> h + [50, 40, 20, 20, 10] + + >>> h = Heap() + >>> h.build_max_heap([1,2,3,4,5,6,7,8,9,0]) + >>> h + [9, 8, 7, 4, 5, 6, 3, 2, 1, 0] + + >>> h = Heap() + >>> h.build_max_heap([514,5,61,57,8,99,105]) + >>> h + [514, 57, 105, 5, 8, 99, 61] + + >>> h = Heap() + >>> h.build_max_heap([514,5,61.6,57,8,9.9,105]) + >>> h + [514, 57, 105, 5, 8, 9.9, 61.6] + """ self.h = list(collection) self.heap_size = len(self.h) if self.heap_size > 1: @@ -108,7 +133,24 @@ def build_max_heap(self, collection: Iterable[T]) -> None: self.max_heapify(i) def extract_max(self) -> T: - """get and remove max from heap""" + """ + get and remove max from heap + + >>> h = Heap() + >>> h.build_max_heap([20,40,50,20,10]) + >>> h.extract_max() + 50 + + >>> h = Heap() + >>> h.build_max_heap([514,5,61,57,8,99,105]) + >>> h.extract_max() + 514 + + >>> h = Heap() + >>> h.build_max_heap([1,2,3,4,5,6,7,8,9,0]) + >>> h.extract_max() + 9 + """ if self.heap_size >= 2: me = self.h[0] self.h[0] = self.h.pop(-1) @@ -122,7 +164,34 @@ def extract_max(self) -> T: raise Exception("Empty heap") def insert(self, value: T) -> None: - """insert a new value into the max heap""" + """ + insert a new value into the max heap + + >>> h = Heap() + >>> h.insert(10) + >>> h + [10] + + >>> h = Heap() + >>> h.insert(10) + >>> h.insert(10) + >>> h + [10, 10] + + >>> h = Heap() + >>> h.insert(10) + >>> h.insert(10.1) + >>> h + [10.1, 10] + + >>> h = Heap() + >>> h.insert(0.1) + >>> h.insert(0) + >>> h.insert(9) + >>> h.insert(5) + >>> h + [9, 5, 0.1, 0] + """ self.h.append(value) idx = (self.heap_size - 1) // 2 self.heap_size += 1 From 5598625c0f086be489c938aa9f9711c337eacf1c Mon Sep 17 00:00:00 2001 From: Suyashd999 Date: Sun, 29 Oct 2023 20:41:12 +0530 Subject: [PATCH 2/4] Added doctest to hash_map.py --- data_structures/hashing/hash_map.py | 126 ++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/data_structures/hashing/hash_map.py b/data_structures/hashing/hash_map.py index 1dfcc8bbf906..c97c97f11fdc 100644 --- a/data_structures/hashing/hash_map.py +++ b/data_structures/hashing/hash_map.py @@ -54,6 +54,26 @@ def _get_next_ind(self, ind: int) -> int: Get next index. Implements linear open addressing. + + Example 1: + >>> hm = HashMap(5) + >>> hm._get_next_ind(3) + 4 + + Example 2: + >>> hm = HashMap(5) + >>> hm._get_next_ind(5) + 1 + + Example 3: + >>> hm = HashMap(5) + >>> hm._get_next_ind(6) + 2 + + Example 4: + >>> hm = HashMap(5) + >>> hm._get_next_ind(9) + 0 """ return (ind + 1) % len(self._buckets) @@ -82,6 +102,16 @@ def _is_full(self) -> bool: Return true if we have reached safe capacity. So we need to increase the number of buckets to avoid collisions. + + >>> hm = HashMap(2) + >>> hm._add_item(1,10) + >>> hm._add_item(2,20) + >>> hm._is_full() + True + + >>> hm = HashMap(2) + >>> hm._is_full() + False """ limit = len(self._buckets) * self._capacity_factor return len(self) >= int(limit) @@ -114,17 +144,107 @@ def _iterate_buckets(self, key: KEY) -> Iterator[int]: ind = self._get_next_ind(ind) def _add_item(self, key: KEY, val: VAL) -> None: + """ + 1. Trying to add 3 elements when size is 5 + >>> hm = HashMap(5) + >>> hm._add_item(1,10) + >>> hm._add_item(2,20) + >>> hm._add_item(3,30) + >>> hm + HashMap(1: 10 ,2: 20 ,3: 30) + + 2. Trying to add 3 elements when size is 5 + >>> hm = HashMap(5) + >>> hm._add_item(-5,10) + >>> hm._add_item(6,30) + >>> hm._add_item(-7,20) + >>> hm + HashMap(-5: 10 ,6: 30 ,-7: 20) + + 3. Trying to add 3 elements when size is 1 + >>> hm = HashMap(1) + >>> hm._add_item(10,13.2) + >>> hm._add_item(6,5.26) + >>> hm._add_item(7,5.155) + >>> hm + HashMap(10: 13.2) + + 4. Trying to add element with key in floating value + >>> hm = HashMap(5) + >>> hm._add_item(1.5,10) + >>> hm + HashMap(1.5: 10) + + 5. Trying to add item with same key + >>> hm = HashMap(5) + >>> hm._add_item(1,10) + >>> hm._add_item(1,20) + >>> hm + HashMap(1: 20) + """ for ind in self._iterate_buckets(key): if self._try_set(ind, key, val): break def __setitem__(self, key: KEY, val: VAL) -> None: + """ + 1. Changing value of item whose key is present + >>> hm = HashMap(5) + >>> hm._add_item(1,10) + >>> hm.__setitem__(1,20) + >>> hm + HashMap(1: 20) + + 2. Changing value of item whose key is not present + >>> hm = HashMap(5) + >>> hm._add_item(1,10) + >>> hm.__setitem__(0,20) + >>> hm + HashMap(0: 20 ,1: 10) + + 3. Changing value of same item multiple times + >>> hm = HashMap(5) + >>> hm._add_item(1,10) + >>> hm.__setitem__(1,20) + >>> hm.__setitem__(1,30) + >>> hm + HashMap(1: 30) + """ if self._is_full(): self._size_up() self._add_item(key, val) def __delitem__(self, key: KEY) -> None: + """ + Example 1. + >>> hm = HashMap(5) + >>> hm._add_item(1,10) + >>> hm._add_item(2,20) + >>> hm._add_item(3,30) + >>> hm.__delitem__(3) + >>> hm + HashMap(1: 10 ,2: 20) + + Example 2. + >>> hm = HashMap(5) + >>> hm._add_item(-5,10) + >>> hm._add_item(6,30) + >>> hm._add_item(-7,20) + >>> hm.__delitem__(-5) + >>> hm + HashMap(6: 30 ,-7: 20) + + Example 3: Trying to remove non-existing item + >>> hm = HashMap(5) + >>> hm._add_item(1,10) + >>> hm._add_item(2,20) + >>> hm._add_item(3,30) + >>> hm.__delitem__(4) + Traceback (most recent call last): + ... + KeyError: 4 + """ for ind in self._iterate_buckets(key): item = self._buckets[ind] if item is None: @@ -160,3 +280,9 @@ def __repr__(self) -> str: f"{item.key}: {item.val}" for item in self._buckets if item ) return f"HashMap({val_string})" + +if __name__ == "__main__": + + import doctest + + doctest.testmod() From 2a29fa02519a2ce337d53054a21264b9d29c0789 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 29 Oct 2023 15:16:12 +0000 Subject: [PATCH 3/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/hashing/hash_map.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/hashing/hash_map.py b/data_structures/hashing/hash_map.py index c97c97f11fdc..6f017a10e398 100644 --- a/data_structures/hashing/hash_map.py +++ b/data_structures/hashing/hash_map.py @@ -281,8 +281,8 @@ def __repr__(self) -> str: ) return f"HashMap({val_string})" -if __name__ == "__main__": +if __name__ == "__main__": import doctest doctest.testmod() From 21f85af3d14ad8ff595c55ed3d9e8def8a794bf1 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 29 Oct 2023 16:58:51 +0100 Subject: [PATCH 4/4] Update hash_map.py --- data_structures/hashing/hash_map.py | 113 ++++++++++++---------------- 1 file changed, 48 insertions(+), 65 deletions(-) diff --git a/data_structures/hashing/hash_map.py b/data_structures/hashing/hash_map.py index 6f017a10e398..1689e07afd9f 100644 --- a/data_structures/hashing/hash_map.py +++ b/data_structures/hashing/hash_map.py @@ -54,25 +54,13 @@ def _get_next_ind(self, ind: int) -> int: Get next index. Implements linear open addressing. - - Example 1: - >>> hm = HashMap(5) - >>> hm._get_next_ind(3) + >>> HashMap(5)._get_next_ind(3) 4 - - Example 2: - >>> hm = HashMap(5) - >>> hm._get_next_ind(5) + >>> HashMap(5)._get_next_ind(5) 1 - - Example 3: - >>> hm = HashMap(5) - >>> hm._get_next_ind(6) + >>> HashMap(5)._get_next_ind(6) 2 - - Example 4: - >>> hm = HashMap(5) - >>> hm._get_next_ind(9) + >>> HashMap(5)._get_next_ind(9) 0 """ return (ind + 1) % len(self._buckets) @@ -104,13 +92,11 @@ def _is_full(self) -> bool: So we need to increase the number of buckets to avoid collisions. >>> hm = HashMap(2) - >>> hm._add_item(1,10) - >>> hm._add_item(2,20) + >>> hm._add_item(1, 10) + >>> hm._add_item(2, 20) >>> hm._is_full() True - - >>> hm = HashMap(2) - >>> hm._is_full() + >>> HashMap(2)._is_full() False """ limit = len(self._buckets) * self._capacity_factor @@ -145,40 +131,40 @@ def _iterate_buckets(self, key: KEY) -> Iterator[int]: def _add_item(self, key: KEY, val: VAL) -> None: """ - 1. Trying to add 3 elements when size is 5 + Try to add 3 elements when the size is 5 >>> hm = HashMap(5) - >>> hm._add_item(1,10) - >>> hm._add_item(2,20) - >>> hm._add_item(3,30) + >>> hm._add_item(1, 10) + >>> hm._add_item(2, 20) + >>> hm._add_item(3, 30) >>> hm - HashMap(1: 10 ,2: 20 ,3: 30) + HashMap(1: 10, 2: 20, 3: 30) - 2. Trying to add 3 elements when size is 5 + Try to add 3 elements when the size is 5 >>> hm = HashMap(5) - >>> hm._add_item(-5,10) - >>> hm._add_item(6,30) - >>> hm._add_item(-7,20) + >>> hm._add_item(-5, 10) + >>> hm._add_item(6, 30) + >>> hm._add_item(-7, 20) >>> hm - HashMap(-5: 10 ,6: 30 ,-7: 20) + HashMap(-5: 10, 6: 30, -7: 20) - 3. Trying to add 3 elements when size is 1 + Try to add 3 elements when size is 1 >>> hm = HashMap(1) - >>> hm._add_item(10,13.2) - >>> hm._add_item(6,5.26) - >>> hm._add_item(7,5.155) + >>> hm._add_item(10, 13.2) + >>> hm._add_item(6, 5.26) + >>> hm._add_item(7, 5.155) >>> hm HashMap(10: 13.2) - 4. Trying to add element with key in floating value + Trying to add an element with a key that is a floating point value >>> hm = HashMap(5) - >>> hm._add_item(1.5,10) + >>> hm._add_item(1.5, 10) >>> hm HashMap(1.5: 10) - 5. Trying to add item with same key + 5. Trying to add an item with the same key >>> hm = HashMap(5) - >>> hm._add_item(1,10) - >>> hm._add_item(1,20) + >>> hm._add_item(1, 10) + >>> hm._add_item(1, 20) >>> hm HashMap(1: 20) """ @@ -190,23 +176,23 @@ def __setitem__(self, key: KEY, val: VAL) -> None: """ 1. Changing value of item whose key is present >>> hm = HashMap(5) - >>> hm._add_item(1,10) - >>> hm.__setitem__(1,20) + >>> hm._add_item(1, 10) + >>> hm.__setitem__(1, 20) >>> hm HashMap(1: 20) 2. Changing value of item whose key is not present >>> hm = HashMap(5) - >>> hm._add_item(1,10) - >>> hm.__setitem__(0,20) + >>> hm._add_item(1, 10) + >>> hm.__setitem__(0, 20) >>> hm - HashMap(0: 20 ,1: 10) + HashMap(0: 20, 1: 10) - 3. Changing value of same item multiple times + 3. Changing the value of the same item multiple times >>> hm = HashMap(5) - >>> hm._add_item(1,10) - >>> hm.__setitem__(1,20) - >>> hm.__setitem__(1,30) + >>> hm._add_item(1, 10) + >>> hm.__setitem__(1, 20) + >>> hm.__setitem__(1, 30) >>> hm HashMap(1: 30) """ @@ -217,29 +203,26 @@ def __setitem__(self, key: KEY, val: VAL) -> None: def __delitem__(self, key: KEY) -> None: """ - Example 1. >>> hm = HashMap(5) - >>> hm._add_item(1,10) - >>> hm._add_item(2,20) - >>> hm._add_item(3,30) + >>> hm._add_item(1, 10) + >>> hm._add_item(2, 20) + >>> hm._add_item(3, 30) >>> hm.__delitem__(3) >>> hm - HashMap(1: 10 ,2: 20) - - Example 2. + HashMap(1: 10, 2: 20) >>> hm = HashMap(5) - >>> hm._add_item(-5,10) - >>> hm._add_item(6,30) - >>> hm._add_item(-7,20) + >>> hm._add_item(-5, 10) + >>> hm._add_item(6, 30) + >>> hm._add_item(-7, 20) >>> hm.__delitem__(-5) >>> hm - HashMap(6: 30 ,-7: 20) + HashMap(6: 30, -7: 20) - Example 3: Trying to remove non-existing item + # Trying to remove a non-existing item >>> hm = HashMap(5) - >>> hm._add_item(1,10) - >>> hm._add_item(2,20) - >>> hm._add_item(3,30) + >>> hm._add_item(1, 10) + >>> hm._add_item(2, 20) + >>> hm._add_item(3, 30) >>> hm.__delitem__(4) Traceback (most recent call last): ... @@ -276,7 +259,7 @@ def __iter__(self) -> Iterator[KEY]: yield from (item.key for item in self._buckets if item) def __repr__(self) -> str: - val_string = " ,".join( + val_string = ", ".join( f"{item.key}: {item.val}" for item in self._buckets if item ) return f"HashMap({val_string})"