From 2f86237df849b1bc427173e3ff6dbd5cafd5a1cc Mon Sep 17 00:00:00 2001 From: Raj Date: Wed, 25 Sep 2019 00:07:36 -0700 Subject: [PATCH 1/6] Min head with decrease key functionality --- data_structures/heap/min_heap.py | 140 +++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 data_structures/heap/min_heap.py diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py new file mode 100644 index 000000000000..a61bceb524c1 --- /dev/null +++ b/data_structures/heap/min_heap.py @@ -0,0 +1,140 @@ +# Min head data structure +# with decrease key functionality - in O(log(n)) time + + +class MinHeap: + def __init__(self, array): + self.idx_of_element = {} + self.heap = self.buildHeap(array) + + def getParentIdx(self, idx): + return (idx - 1) // 2 + + def getLeftChildIdx(self, idx): + return idx * 2 + 1 + + def getRightChildIdx(self, idx): + return idx * 2 + 2 + + def buildHeap(self, array): + lastIdx = len(array) - 1 + startFrom = self.getParentIdx(lastIdx) + + for idx, i in enumerate(array): + self.idx_of_element[i] = idx + + for i in range(startFrom, -1, -1): + self.siftDown(i, array) + return array + + # this is min-heapify method + def siftDown(self, idx, array): + while True: + l = self.getLeftChildIdx(idx) + r = self.getRightChildIdx(idx) + + smallest = idx + if l < len(array) and array[l] < array[idx]: + smallest = l + if r < len(array) and array[r] < array[smallest]: + smallest = r + + if smallest != idx: + array[idx], array[smallest] = array[smallest], array[idx] + self.idx_of_element[array[idx]], self.idx_of_element[ + array[smallest] + ] = ( + self.idx_of_element[array[smallest]], + self.idx_of_element[array[idx]], + ) + idx = smallest + else: + break + + def siftUp(self, idx): + p = self.getParentIdx(idx) + while p >= 0 and self.heap[p] > self.heap[idx]: + self.heap[p], self.heap[idx] = self.heap[idx], self.heap[p] + self.idx_of_element[self.heap[p]], self.idx_of_element[self.heap[idx]] = ( + self.idx_of_element[self.heap[idx]], + self.idx_of_element[self.heap[p]], + ) + idx = p + p = self.getParentIdx(idx) + + def peek(self): + return self.heap[0] + + def remove(self): + self.heap[0], self.heap[-1] = self.heap[-1], self.heap[0] + self.idx_of_element[self.heap[0]], self.idx_of_element[self.heap[-1]] = ( + self.idx_of_element[self.heap[-1]], + self.idx_of_element[self.heap[0]], + ) + + x = self.heap.pop() + del self.idx_of_element[x] + self.siftDown(0, self.heap) + return x + + def insert(self, value): + self.heap.append(value) + self.idx_of_element[value] = len(self.heap) - 1 + self.siftUp(len(self.heap) - 1) + + def isEmpty(self): + return True if len(self.heap) == 0 else False + + def decreaseKey(self, key, newValue): + assert ( + self.heap[self.idx_of_element[key]].val > newValue + ), "newValue must be less that current value" + key.val = newValue + self.siftUp(self.idx_of_element[key]) + + +class Node: + def __init__(self, val, name): + self.val = val + self.name = name + + def __str__(self): + return self.name + + def __lt__(self, other): + return self.val < other.val + + +## USAGE + +r = Node(-1, "R") +b = Node(6, "B") +a = Node(3, "A") +x = Node(1, "X") +e = Node(4, "E") + +arr = [r, b, a, x, e] + +# Use one of these two ways to generate Min-Heap + +# Generating Min-Heap from array +myMinHeap = MinHeap(arr) + +# Generating Min-Heap by Insert method +# myMinHeap.insert(a) +# myMinHeap.insert(b) +# myMinHeap.insert(x) +# myMinHeap.insert(r) +# myMinHeap.insert(e) + +# Before +print("Min Heap - before decrease key") +for i in myMinHeap.heap: + print(i, i.val) + +print("Min Heap - After decrease key of node [B -> -17]") +myMinHeap.decreaseKey(b, -17) + +# After +for i in myMinHeap.heap: + print(i, i.val) From 67cfb523b7f4a417665986d01d658e62ee33c605 Mon Sep 17 00:00:00 2001 From: Raj Date: Wed, 25 Sep 2019 00:44:03 -0700 Subject: [PATCH 2/6] doctest added --- data_structures/heap/min_heap.py | 82 +++++++++++++++++++------------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py index a61bceb524c1..a8672294cc4c 100644 --- a/data_structures/heap/min_heap.py +++ b/data_structures/heap/min_heap.py @@ -2,36 +2,58 @@ # with decrease key functionality - in O(log(n)) time +class Node: + def __init__(self, val, name): + self.val = val + self.name = name + + def __str__(self): + return self.name + + def __lt__(self, other): + return self.val < other.val + + class MinHeap: + """ + >>> r = Node(-1, "R") + >>> b = Node(6, "B") + >>> a = Node(3, "A") + >>> x = Node(1, "X") + >>> e = Node(4, "E") + >>> myMinHeap = MinHeap([r, b, a, x, e]) + >>> myMinHeap.decrease_key(b, -17) + """ + def __init__(self, array): self.idx_of_element = {} - self.heap = self.buildHeap(array) + self.heap = self.build_heap(array) - def getParentIdx(self, idx): + def get_parent_idx(self, idx): return (idx - 1) // 2 - def getLeftChildIdx(self, idx): + def get_left_child_idx(self, idx): return idx * 2 + 1 - def getRightChildIdx(self, idx): + def get_right_child_idx(self, idx): return idx * 2 + 2 - def buildHeap(self, array): + def build_heap(self, array): lastIdx = len(array) - 1 - startFrom = self.getParentIdx(lastIdx) + startFrom = self.get_parent_idx(lastIdx) for idx, i in enumerate(array): self.idx_of_element[i] = idx for i in range(startFrom, -1, -1): - self.siftDown(i, array) + self.sift_down(i, array) return array # this is min-heapify method - def siftDown(self, idx, array): + def sift_down(self, idx, array): while True: - l = self.getLeftChildIdx(idx) - r = self.getRightChildIdx(idx) + l = self.get_left_child_idx(idx) + r = self.get_right_child_idx(idx) smallest = idx if l < len(array) and array[l] < array[idx]: @@ -51,8 +73,8 @@ def siftDown(self, idx, array): else: break - def siftUp(self, idx): - p = self.getParentIdx(idx) + def sift_up(self, idx): + p = self.get_parent_idx(idx) while p >= 0 and self.heap[p] > self.heap[idx]: self.heap[p], self.heap[idx] = self.heap[idx], self.heap[p] self.idx_of_element[self.heap[p]], self.idx_of_element[self.heap[idx]] = ( @@ -60,7 +82,7 @@ def siftUp(self, idx): self.idx_of_element[self.heap[p]], ) idx = p - p = self.getParentIdx(idx) + p = self.get_parent_idx(idx) def peek(self): return self.heap[0] @@ -74,35 +96,23 @@ def remove(self): x = self.heap.pop() del self.idx_of_element[x] - self.siftDown(0, self.heap) + self.sift_down(0, self.heap) return x def insert(self, value): self.heap.append(value) self.idx_of_element[value] = len(self.heap) - 1 - self.siftUp(len(self.heap) - 1) + self.sift_up(len(self.heap) - 1) - def isEmpty(self): + def is_empty(self): return True if len(self.heap) == 0 else False - def decreaseKey(self, key, newValue): + def decrease_key(self, key, newValue): assert ( self.heap[self.idx_of_element[key]].val > newValue ), "newValue must be less that current value" key.val = newValue - self.siftUp(self.idx_of_element[key]) - - -class Node: - def __init__(self, val, name): - self.val = val - self.name = name - - def __str__(self): - return self.name - - def __lt__(self, other): - return self.val < other.val + self.sift_up(self.idx_of_element[key]) ## USAGE @@ -113,12 +123,10 @@ def __lt__(self, other): x = Node(1, "X") e = Node(4, "E") -arr = [r, b, a, x, e] - # Use one of these two ways to generate Min-Heap # Generating Min-Heap from array -myMinHeap = MinHeap(arr) +myMinHeap = MinHeap([r, b, a, x, e]) # Generating Min-Heap by Insert method # myMinHeap.insert(a) @@ -133,8 +141,14 @@ def __lt__(self, other): print(i, i.val) print("Min Heap - After decrease key of node [B -> -17]") -myMinHeap.decreaseKey(b, -17) +myMinHeap.decrease_key(b, -17) # After for i in myMinHeap.heap: print(i, i.val) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From a871188b3b20ca2cf87184d19edc859876766883 Mon Sep 17 00:00:00 2001 From: Raj Date: Wed, 25 Sep 2019 11:52:29 -0700 Subject: [PATCH 3/6] __str__ changed as per Python convention --- data_structures/heap/min_heap.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py index a8672294cc4c..d54787e3f1de 100644 --- a/data_structures/heap/min_heap.py +++ b/data_structures/heap/min_heap.py @@ -3,12 +3,12 @@ class Node: - def __init__(self, val, name): - self.val = val + def __init__(self, name, val): self.name = name + self.val = val def __str__(self): - return self.name + return f"{self.__class__.__name__}({self.name}, {self.val})" def __lt__(self, other): return self.val < other.val @@ -16,11 +16,11 @@ def __lt__(self, other): class MinHeap: """ - >>> r = Node(-1, "R") - >>> b = Node(6, "B") - >>> a = Node(3, "A") - >>> x = Node(1, "X") - >>> e = Node(4, "E") + >>> r = Node("R", -1) + >>> b = Node("B", 6) + >>> a = Node("A", 3) + >>> x = Node("X", 1) + >>> e = Node("E", 4) >>> myMinHeap = MinHeap([r, b, a, x, e]) >>> myMinHeap.decrease_key(b, -17) """ @@ -117,11 +117,11 @@ def decrease_key(self, key, newValue): ## USAGE -r = Node(-1, "R") -b = Node(6, "B") -a = Node(3, "A") -x = Node(1, "X") -e = Node(4, "E") +r = Node("R", -1) +b = Node("B", 6) +a = Node("A", 3) +x = Node("X", 1) +e = Node("E", 4) # Use one of these two ways to generate Min-Heap @@ -138,14 +138,14 @@ def decrease_key(self, key, newValue): # Before print("Min Heap - before decrease key") for i in myMinHeap.heap: - print(i, i.val) + print(i) print("Min Heap - After decrease key of node [B -> -17]") myMinHeap.decrease_key(b, -17) # After for i in myMinHeap.heap: - print(i, i.val) + print(i) if __name__ == "__main__": From 853ca2ca7b1bafad3f3373e0ba7486dee6b57414 Mon Sep 17 00:00:00 2001 From: Raj Date: Wed, 25 Sep 2019 12:09:26 -0700 Subject: [PATCH 4/6] edits in doctest --- data_structures/heap/min_heap.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py index d54787e3f1de..7b1f5d294027 100644 --- a/data_structures/heap/min_heap.py +++ b/data_structures/heap/min_heap.py @@ -21,8 +21,12 @@ class MinHeap: >>> a = Node("A", 3) >>> x = Node("X", 1) >>> e = Node("E", 4) + >>> print(b) + Node(B, 6) >>> myMinHeap = MinHeap([r, b, a, x, e]) >>> myMinHeap.decrease_key(b, -17) + >>> print(b) + Node(B, -17) """ def __init__(self, array): From bfebbf5659bb4bdf2c1cdc656e637495983509d8 Mon Sep 17 00:00:00 2001 From: Raj Date: Wed, 25 Sep 2019 12:26:15 -0700 Subject: [PATCH 5/6] get_value by key added --- data_structures/heap/min_heap.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py index 7b1f5d294027..97a68d1c4dab 100644 --- a/data_structures/heap/min_heap.py +++ b/data_structures/heap/min_heap.py @@ -27,10 +27,13 @@ class MinHeap: >>> myMinHeap.decrease_key(b, -17) >>> print(b) Node(B, -17) + >>> print(myMinHeap.get_value("B")) + -17 """ def __init__(self, array): self.idx_of_element = {} + self.heap_dict = {} self.heap = self.build_heap(array) def get_parent_idx(self, idx): @@ -42,12 +45,16 @@ def get_left_child_idx(self, idx): def get_right_child_idx(self, idx): return idx * 2 + 2 + def get_value(self, key): + return self.heap_dict[key] + def build_heap(self, array): lastIdx = len(array) - 1 startFrom = self.get_parent_idx(lastIdx) for idx, i in enumerate(array): self.idx_of_element[i] = idx + self.heap_dict[i.name] = i.val for i in range(startFrom, -1, -1): self.sift_down(i, array) @@ -103,20 +110,22 @@ def remove(self): self.sift_down(0, self.heap) return x - def insert(self, value): - self.heap.append(value) - self.idx_of_element[value] = len(self.heap) - 1 + def insert(self, node): + self.heap.append(node) + self.idx_of_element[node] = len(self.heap) - 1 + self.heap_dict[node.name] = node.val self.sift_up(len(self.heap) - 1) def is_empty(self): return True if len(self.heap) == 0 else False - def decrease_key(self, key, newValue): + def decrease_key(self, node, newValue): assert ( - self.heap[self.idx_of_element[key]].val > newValue + self.heap[self.idx_of_element[node]].val > newValue ), "newValue must be less that current value" - key.val = newValue - self.sift_up(self.idx_of_element[key]) + node.val = newValue + self.heap_dict[node.name] = newValue + self.sift_up(self.idx_of_element[node]) ## USAGE @@ -151,7 +160,6 @@ def decrease_key(self, key, newValue): for i in myMinHeap.heap: print(i) - if __name__ == "__main__": import doctest From 6741f4942fdf066dbe739d2cb570a186c246fb53 Mon Sep 17 00:00:00 2001 From: Raj Date: Wed, 25 Sep 2019 14:54:59 -0700 Subject: [PATCH 6/6] __getitem__ added --- data_structures/heap/min_heap.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py index 97a68d1c4dab..6184d83be774 100644 --- a/data_structures/heap/min_heap.py +++ b/data_structures/heap/min_heap.py @@ -27,7 +27,7 @@ class MinHeap: >>> myMinHeap.decrease_key(b, -17) >>> print(b) Node(B, -17) - >>> print(myMinHeap.get_value("B")) + >>> print(myMinHeap["B"]) -17 """ @@ -36,6 +36,9 @@ def __init__(self, array): self.heap_dict = {} self.heap = self.build_heap(array) + def __getitem__(self, key): + return self.get_value(key) + def get_parent_idx(self, idx): return (idx - 1) // 2