|
| 1 | +from pydatastructs.utils.misc_util import _check_type, NoneType, TreeNode |
| 2 | +from pydatastructs.linear_data_structures.arrays import ArrayForTrees |
| 3 | + |
| 4 | +__all__ = [ |
| 5 | + 'BinaryHeap' |
| 6 | +] |
| 7 | + |
| 8 | +class Heap: |
| 9 | + """ |
| 10 | + Abstract class for representing heaps. |
| 11 | + """ |
| 12 | + pass |
| 13 | + |
| 14 | +class BinaryHeap: |
| 15 | + """ |
| 16 | + Represents Binary Heap. |
| 17 | +
|
| 18 | + Parameters |
| 19 | + ========== |
| 20 | +
|
| 21 | + elements : list, tuple |
| 22 | + Optional, by default 'None'. |
| 23 | + List/tuple of initial elements in Heap. |
| 24 | +
|
| 25 | + heap_property : str |
| 26 | + The property of binary heap. |
| 27 | + If the key stored in each node is |
| 28 | + either greater than or equal to |
| 29 | + the keys in the node's children |
| 30 | + then pass 'max'. |
| 31 | + If the key stored in each node is |
| 32 | + either less than or equal to |
| 33 | + the keys in the node's children |
| 34 | + then pass 'min'. |
| 35 | + By default, the heap property is |
| 36 | + set to 'min'. |
| 37 | +
|
| 38 | + Examples |
| 39 | + ======== |
| 40 | +
|
| 41 | + >>> from pydatastructs.trees.heaps import BinaryHeap |
| 42 | + >>> min_heap = BinaryHeap(heap_property="min") |
| 43 | + >>> min_heap.insert(1, 1) |
| 44 | + >>> min_heap.insert(5, 5) |
| 45 | + >>> min_heap.insert(7, 7) |
| 46 | + >>> min_heap.extract().key |
| 47 | + 1 |
| 48 | + >>> min_heap.insert(4, 4) |
| 49 | + >>> min_heap.extract().key |
| 50 | + 4 |
| 51 | +
|
| 52 | + >>> max_heap = BinaryHeap(heap_property='max') |
| 53 | + >>> max_heap.insert(1, 1) |
| 54 | + >>> max_heap.insert(5, 5) |
| 55 | + >>> max_heap.insert(7, 7) |
| 56 | + >>> max_heap.extract().key |
| 57 | + 7 |
| 58 | + >>> max_heap.insert(6, 6) |
| 59 | + >>> max_heap.extract().key |
| 60 | + 6 |
| 61 | +
|
| 62 | + References |
| 63 | + ========== |
| 64 | +
|
| 65 | + .. [1] https://en.m.wikipedia.org/wiki/Binary_heap |
| 66 | + """ |
| 67 | + __slots__ = ['_comp', 'heap', 'heap_property', '_last_pos_filled'] |
| 68 | + |
| 69 | + def __new__(cls, elements=None, heap_property="min"): |
| 70 | + obj = object.__new__(cls) |
| 71 | + obj.heap_property = heap_property |
| 72 | + if heap_property == "min": |
| 73 | + obj._comp = lambda key_parent, key_child: key_parent <= key_child |
| 74 | + elif heap_property == "max": |
| 75 | + obj._comp = lambda key_parent, key_child: key_parent >= key_child |
| 76 | + else: |
| 77 | + raise ValueError("%s is invalid heap property"%(heap_property)) |
| 78 | + if elements is None: |
| 79 | + elements = [] |
| 80 | + obj.heap = elements |
| 81 | + obj._last_pos_filled = len(elements) - 1 |
| 82 | + obj._build() |
| 83 | + return obj |
| 84 | + |
| 85 | + def _build(self): |
| 86 | + for i in range(self._last_pos_filled + 1): |
| 87 | + self.heap[i].left, self.heap[i].right = \ |
| 88 | + 2*i + 1, 2*i + 2 |
| 89 | + for i in range((self._last_pos_filled + 1)//2, -1, -1): |
| 90 | + self._heapify(i) |
| 91 | + |
| 92 | + def _swap(self, idx1, idx2): |
| 93 | + idx1_key, idx1_data = \ |
| 94 | + self.heap[idx1].key, self.heap[idx1].data |
| 95 | + self.heap[idx1].key, self.heap[idx1].data = \ |
| 96 | + self.heap[idx2].key, self.heap[idx2].data |
| 97 | + self.heap[idx2].key, self.heap[idx2].data = \ |
| 98 | + idx1_key, idx1_data |
| 99 | + |
| 100 | + def _heapify(self, i): |
| 101 | + target = i |
| 102 | + l = 2*i + 1 |
| 103 | + r = 2*i + 2 |
| 104 | + |
| 105 | + if l <= self._last_pos_filled: |
| 106 | + target = l if self._comp(self.heap[l].key, self.heap[target].key) \ |
| 107 | + else i |
| 108 | + if r <= self._last_pos_filled: |
| 109 | + target = r if self._comp(self.heap[r].key, self.heap[target].key) \ |
| 110 | + else target |
| 111 | + |
| 112 | + if target != i: |
| 113 | + self._swap(target, i) |
| 114 | + i = target |
| 115 | + self._heapify(i) |
| 116 | + |
| 117 | + def insert(self, key, data): |
| 118 | + """ |
| 119 | + Insert a new element to the heap according to heap property. |
| 120 | +
|
| 121 | + Parameters |
| 122 | + ========== |
| 123 | +
|
| 124 | + key |
| 125 | + The key for comparison. |
| 126 | + data |
| 127 | + The data to be inserted. |
| 128 | +
|
| 129 | + Returns |
| 130 | + ======= |
| 131 | +
|
| 132 | + None |
| 133 | + """ |
| 134 | + new_node = TreeNode(key, data) |
| 135 | + self.heap.append(new_node) |
| 136 | + self._last_pos_filled += 1 |
| 137 | + i = self._last_pos_filled |
| 138 | + self.heap[i].left, self.heap[i].right = 2*i + 1, 2*i + 2 |
| 139 | + |
| 140 | + while True: |
| 141 | + parent = (i - 1)//2 |
| 142 | + if i == 0 or self._comp(self.heap[parent].key, self.heap[i].key): |
| 143 | + break |
| 144 | + else: |
| 145 | + self._swap(i, parent) |
| 146 | + i = parent |
| 147 | + |
| 148 | + def extract(self): |
| 149 | + """ |
| 150 | + Extract root element of the Heap. |
| 151 | +
|
| 152 | + Returns |
| 153 | + ======= |
| 154 | +
|
| 155 | + root_element : TreeNode |
| 156 | + The TreeNode at the root of the heap, |
| 157 | + if the heap is not empty. |
| 158 | + None |
| 159 | + If the heap is empty. |
| 160 | + """ |
| 161 | + if self._last_pos_filled == -1: |
| 162 | + return None |
| 163 | + else: |
| 164 | + element_to_be_extracted = TreeNode(self.heap[0].key, self.heap[0].data) |
| 165 | + self._swap(0, self._last_pos_filled) |
| 166 | + self.heap[self._last_pos_filled] = TreeNode(float('inf') if self.heap_property == 'min' |
| 167 | + else float('-inf'), None) |
| 168 | + self._heapify(0) |
| 169 | + self.heap.pop() |
| 170 | + self._last_pos_filled -= 1 |
| 171 | + return element_to_be_extracted |
| 172 | + |
| 173 | + def __str__(self): |
| 174 | + to_be_printed = ['' for i in range(self._last_pos_filled + 1)] |
| 175 | + for i in range(self._last_pos_filled + 1): |
| 176 | + node = self.heap[i] |
| 177 | + to_be_printed[i] = (node.left if node.left <= self._last_pos_filled else None, |
| 178 | + node.key, node.data, |
| 179 | + node.right if node.right <= self._last_pos_filled else None) |
| 180 | + return str(to_be_printed) |
0 commit comments