From 59aec2557158cb8ab2a78e525378c9a7ce9db8e9 Mon Sep 17 00:00:00 2001 From: KirilBangachev <51961981+KirilBangachev@users.noreply.github.com> Date: Thu, 22 Aug 2019 13:47:40 +0300 Subject: [PATCH 1/7] Binomial Heap Implementation of Binomial Heap. Reference: Advanced Data Structures, Peter Brass --- data_structures/heap/binomial_heap.py | 361 ++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 data_structures/heap/binomial_heap.py diff --git a/data_structures/heap/binomial_heap.py b/data_structures/heap/binomial_heap.py new file mode 100644 index 000000000000..31086667f335 --- /dev/null +++ b/data_structures/heap/binomial_heap.py @@ -0,0 +1,361 @@ +""" + Min-oriented priority queue implemented with the Binomial Heap data + structure. +""" + + +class BinomialHeap: + class Node: + """ + Node in a doubly-linked binomial tree, containing: + - value + - size of left subtree + - link to left, right and parent nodes + """ + + def __init__(self, val): + """ + Constructor for Node with value val. + Left, right and parent links are initiated as None + """ + self.val = val + self.left_tree_size = ( + 0 + ) # Number of nodes in left subtree + self.left = None + self.right = None + self.parent = None + + def mergeTrees(self, other): + """ + In-place merge of two binomial trees of equal size. + Returns the root of the resulting tree + """ + assert ( + self.left_tree_size == other.left_tree_size + ), "Unequal Sizes of Blocks" + + if self.val < other.val: + other.left = self.right + other.parent = None + if self.right: + self.right.parent = other + self.right = other + self.left_tree_size = ( + self.left_tree_size * 2 + 1 + ) + return self + else: + self.left = other.right + self.parent = None + if other.right: + other.right.parent = self + other.right = self + other.left_tree_size = ( + other.left_tree_size * 2 + 1 + ) + return other + + def __init__( + self, bottom_root=None, min_node=None, heap_size=0 + ): + """ + Empty BinomialHeap + """ + self.size = heap_size + self.bottom_root = bottom_root + self.min_node = min_node + + def mergeHeaps(self, other): + """ + Merge two heaps in-place + """ + + # Empty heaps corner cases + if other.size == 0: + return + if self.size == 0: + self.size = other.size + self.bottom_root = other.bottom_root + self.min_node = other.min_node + return + # Update size + self.size = self.size + other.size + + # Update min.node + if self.min_node.val > other.min_node.val: + self.min_node = other.min_node + # Merge + + # Order roots by left_subtree_size + combined_roots_list = [] + i, j = self.bottom_root, other.bottom_root + while i or j: + if i and ( + (not j) + or i.left_tree_size < j.left_tree_size + ): + combined_roots_list.append((i, True)) + i = i.parent + else: + combined_roots_list.append((j, False)) + j = j.parent + # Insert links between them + for i in range(len(combined_roots_list) - 1): + if ( + combined_roots_list[i][1] + != combined_roots_list[i + 1][1] + ): + combined_roots_list[i][ + 0 + ].parent = combined_roots_list[i + 1][0] + combined_roots_list[i + 1][ + 0 + ].left = combined_roots_list[i][0] + # Consecutively merge roots with same left_tree_size + i = combined_roots_list[0][0] + while i.parent: + if ( + ( + i.left_tree_size + == i.parent.left_tree_size + ) + and (not i.parent.parent) + ) or ( + i.left_tree_size == i.parent.left_tree_size + and i.left_tree_size + != i.parent.parent.left_tree_size + ): + + # Neighbouring Nodes + previous_node = i.left + next_node = i.parent.parent + + # Merging trees + i = i.mergeTrees(i.parent) + + # Updating links + i.left = previous_node + i.parent = next_node + if previous_node: + previous_node.parent = i + if next_node: + next_node.left = i + else: + i = i.parent + # Updating self.bottom_root + while i.left: + i = i.left + self.bottom_root = i + + def insert(self, val): + """ + Insert a value val in heap. + """ + if self.size == 0: + self.bottom_root = Node(val) + self.size = 1 + self.min_node = self.bottom_root + else: + # Create new node + new_node = Node(val) + + # Update size + self.size += 1 + + # update min_node + if val < self.min_node.val: + self.min_node = new_node + # Put new_node as a bottom_root in heap + self.bottom_root.left = new_node + new_node.parent = self.bottom_root + self.bottom_root = new_node + + # Consecutively merge roots with same left_tree_size + while ( + self.bottom_root.parent + and self.bottom_root.left_tree_size + == self.bottom_root.parent.left_tree_size + ): + + # Next node + next_node = self.bottom_root.parent.parent + + # Merge + self.bottom_root = self.bottom_root.mergeTrees( + self.bottom_root.parent + ) + + # Update Links + self.bottom_root.parent = next_node + self.bottom_root.left = None + if next_node: + next_node.left = self.bottom_root + + def peek(self): + """ + Return minimal element without deleting it + """ + return self.min_node.val + + def isEmpty(self): + """ + Check if heap is Empty + """ + return self.size == 0 + + def deleteMin(self): + """ + Delete minimal element and return it + """ + assert not self.isEmpty(), "Empty Heap" + + # Save minimal value + min_value = self.min_node.val + + # Last element in heap corner case + if self.size == 1: + # Update size + self.size = 0 + + # Update bottom root + self.bottom_root = None + + # Update min_node + self.min_node = None + + return min_value + # No right subtree corner case + # The structure of the tree implies that this should be the bottom root + # and there is at least one other root + if self.min_node.right == None: + # Update size + self.size -= 1 + + # Update bottom root + self.bottom_root = self.bottom_root.parent + self.bottom_root.left = None + + # Update min_node + self.min_node = self.bottom_root + i = self.bottom_root.parent + while i: + if i.val < self.min_node.val: + self.min_node = i + i = i.parent + return min_value + # General case + # Find the BinomialHeap of the right subtree of min_node + bottom_of_new = self.min_node.right + bottom_of_new.parent = None + min_of_new = bottom_of_new + size_of_new = 1 + + # Size, min_node and bottom_root + while bottom_of_new.left: + size_of_new = size_of_new * 2 + 1 + bottom_of_new = bottom_of_new.left + if bottom_of_new.val < min_of_new.val: + min_of_new = bottom_of_new + # Corner case of single root on top left path + if (not self.min_node.left) and ( + not self.min_node.parent + ): + self.size = size_of_new + self.bottom_root = bottom_of_new + self.min_node = min_of_new + # print("Single root, multiple nodes case") + return min_value + # Remaining cases + # Construct heap of right subtree + newHeap = BinomialHeap( + bottom_root=bottom_of_new, + min_node=min_of_new, + heap_size=size_of_new, + ) + + # Update size + self.size = self.size - 1 - size_of_new + + # Neighbour nodes + previous_node = self.min_node.left + next_node = self.min_node.parent + + # Initialize new bottom_root and min_node + self.min_node = previous_node or next_node + self.bottom_root = next_node + + # Update links of previous_node and search below for new min_node and + # bottom_root + if previous_node: + previous_node.parent = next_node + + # Update bottom_root and search for min_node below + self.bottom_root = previous_node + self.min_node = previous_node + while self.bottom_root.left: + self.bottom_root = self.bottom_root.left + if self.bottom_root.val < self.min_node.val: + self.min_node = self.bottom_root + if next_node: + next_node.left = previous_node + + # Search for new min_node above min_node + i = next_node + while i: + if i.val < self.min_node.val: + self.min_node = i + i = i.parent + # Merge heaps + self.mergeHeaps(newHeap) + + return min_value + + def __Print(self, curr_node, level=0): + """ + Pre-order traversal of nodes with print + """ + if curr_node: + print( + "-" * level, end=str(curr_node.val) + "\n" + ) + self.__Print(curr_node.left, level + 1) + self.__Print(curr_node.right, level + 1) + else: + print("-" * level, end="# \n") + + def NodePrint(self): + """ + Pre-order prining of heap tree + """ + assert not self.isEmpty(), "Empty Heap" + + # Find top root + i = self.bottom_root + while i.parent: + i = i.parent + # Print + self.__Print(i) + + +#%%% + +# Unit Tests + +# A random permutation of 30 integers to be inserted +import numpy as np + +permutation = np.random.permutation(list(range(30))) + + +TestHeap = BinomialHeap() + +# 30 inserts +for number in permutation: + TestHeap.insert(number) +# Printing Heap +TestHeap.NodePrint() + +# Deleting +while 1: + print(TestHeap.deleteMin(), end=" ") # 0, 1, 2, 3, ... From 949dcd983a4d1ef7d36aae62c3f7a4082d7422b1 Mon Sep 17 00:00:00 2001 From: KirilBangachev <51961981+KirilBangachev@users.noreply.github.com> Date: Thu, 22 Aug 2019 15:24:59 +0300 Subject: [PATCH 2/7] Update binomial_heap.py --- data_structures/heap/binomial_heap.py | 90 +++++++++++++-------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/data_structures/heap/binomial_heap.py b/data_structures/heap/binomial_heap.py index 31086667f335..f620ee78c1e4 100644 --- a/data_structures/heap/binomial_heap.py +++ b/data_structures/heap/binomial_heap.py @@ -1,60 +1,60 @@ -""" - Min-oriented priority queue implemented with the Binomial Heap data - structure. -""" - - -class BinomialHeap: - class Node: - """ +class Node: + """ Node in a doubly-linked binomial tree, containing: - value - size of left subtree - link to left, right and parent nodes """ - def __init__(self, val): - """ + def __init__(self, val): + """ Constructor for Node with value val. Left, right and parent links are initiated as None """ - self.val = val - self.left_tree_size = ( - 0 - ) # Number of nodes in left subtree - self.left = None - self.right = None - self.parent = None - - def mergeTrees(self, other): - """ + self.val = val + self.left_tree_size = ( + 0 + ) # Number of nodes in left subtree + self.left = None + self.right = None + self.parent = None + + def mergeTrees(self, other): + """ In-place merge of two binomial trees of equal size. Returns the root of the resulting tree """ - assert ( - self.left_tree_size == other.left_tree_size - ), "Unequal Sizes of Blocks" - - if self.val < other.val: - other.left = self.right - other.parent = None - if self.right: - self.right.parent = other - self.right = other - self.left_tree_size = ( - self.left_tree_size * 2 + 1 - ) - return self - else: - self.left = other.right - self.parent = None - if other.right: - other.right.parent = self - other.right = self - other.left_tree_size = ( - other.left_tree_size * 2 + 1 - ) - return other + assert ( + self.left_tree_size == other.left_tree_size + ), "Unequal Sizes of Blocks" + + if self.val < other.val: + other.left = self.right + other.parent = None + if self.right: + self.right.parent = other + self.right = other + self.left_tree_size = ( + self.left_tree_size * 2 + 1 + ) + return self + else: + self.left = other.right + self.parent = None + if other.right: + other.right.parent = self + other.right = self + other.left_tree_size = ( + other.left_tree_size * 2 + 1 + ) + return other + + +class BinomialHeap: + """ + Min-oriented priority queue implemented with the Binomial Heap data + structure. + """ def __init__( self, bottom_root=None, min_node=None, heap_size=0 From abc1fd486afc42b93bd800a6003bfdee2dfe6fa4 Mon Sep 17 00:00:00 2001 From: KirilBangachev <51961981+KirilBangachev@users.noreply.github.com> Date: Thu, 22 Aug 2019 15:34:47 +0300 Subject: [PATCH 3/7] Update binomial_heap.py --- data_structures/heap/binomial_heap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data_structures/heap/binomial_heap.py b/data_structures/heap/binomial_heap.py index f620ee78c1e4..c2d83ad0e835 100644 --- a/data_structures/heap/binomial_heap.py +++ b/data_structures/heap/binomial_heap.py @@ -357,5 +357,5 @@ def NodePrint(self): TestHeap.NodePrint() # Deleting -while 1: - print(TestHeap.deleteMin(), end=" ") # 0, 1, 2, 3, ... +for i in range(20): + print(TestHeap.deleteMin(), end=" ") # 0, 1, 2, 3, ... , 19 From c9ae55087f825d191751bb14789c68f067448f5d Mon Sep 17 00:00:00 2001 From: KirilBangachev <51961981+KirilBangachev@users.noreply.github.com> Date: Tue, 3 Sep 2019 18:11:26 +0300 Subject: [PATCH 4/7] Update binomial_heap.py - Fuller documentation of binomial heap - Update unit tests - Replace printing method by overwriting __str__() --- data_structures/heap/binomial_heap.py | 135 +++++++++++++------------- 1 file changed, 65 insertions(+), 70 deletions(-) diff --git a/data_structures/heap/binomial_heap.py b/data_structures/heap/binomial_heap.py index c2d83ad0e835..4c87e9c2103d 100644 --- a/data_structures/heap/binomial_heap.py +++ b/data_structures/heap/binomial_heap.py @@ -1,20 +1,32 @@ +""" + Min-oriented priority queue implemented with the Binomial Heap data + structure implemented with the BinomialHeap class. There is also a helper + class Node. + + Performance details: + - Insert element in a heap with n elemnts: Guaranteed logn, amoratized 1 + - Merge (meld) heaps of size m and n: O(logn + logm) + - Delete Min: O(logn) + + Inserting and merging performance are the main advantages over a binary heap + + Reference: Advanced Data Structures, Peter Brass +""" + + class Node: """ - Node in a doubly-linked binomial tree, containing: - - value - - size of left subtree - - link to left, right and parent nodes - """ + Node in a doubly-linked binomial tree, containing: + - value + - size of left subtree + - link to left, right and parent nodes + """ def __init__(self, val): - """ - Constructor for Node with value val. - Left, right and parent links are initiated as None - """ self.val = val - self.left_tree_size = ( + self.left_tree_size = ( # Number of nodes in left subtree 0 - ) # Number of nodes in left subtree + ) self.left = None self.right = None self.parent = None @@ -51,25 +63,14 @@ def mergeTrees(self, other): class BinomialHeap: - """ - Min-oriented priority queue implemented with the Binomial Heap data - structure. - """ - def __init__( self, bottom_root=None, min_node=None, heap_size=0 ): - """ - Empty BinomialHeap - """ self.size = heap_size self.bottom_root = bottom_root self.min_node = min_node def mergeHeaps(self, other): - """ - Merge two heaps in-place - """ # Empty heaps corner cases if other.size == 0: @@ -149,9 +150,6 @@ def mergeHeaps(self, other): self.bottom_root = i def insert(self, val): - """ - Insert a value val in heap. - """ if self.size == 0: self.bottom_root = Node(val) self.size = 1 @@ -193,21 +191,12 @@ def insert(self, val): next_node.left = self.bottom_root def peek(self): - """ - Return minimal element without deleting it - """ return self.min_node.val def isEmpty(self): - """ - Check if heap is Empty - """ return self.size == 0 def deleteMin(self): - """ - Delete minimal element and return it - """ assert not self.isEmpty(), "Empty Heap" # Save minimal value @@ -311,51 +300,57 @@ def deleteMin(self): return min_value - def __Print(self, curr_node, level=0): + def __traversal(self, curr_node, preorder, level=0): """ - Pre-order traversal of nodes with print + Pre-order traversal of nodes """ if curr_node: - print( - "-" * level, end=str(curr_node.val) + "\n" + preorder.append((curr_node.val, level)) + self.__traversal( + curr_node.left, preorder, level + 1 + ) + self.__traversal( + curr_node.right, preorder, level + 1 ) - self.__Print(curr_node.left, level + 1) - self.__Print(curr_node.right, level + 1) else: - print("-" * level, end="# \n") + preorder.append(("#", level)) - def NodePrint(self): + def __str__(self): """ - Pre-order prining of heap tree + Overwriting str for a pre-order print of nodes in heap; + Performance is poor, so use only for small examples """ - assert not self.isEmpty(), "Empty Heap" - + if self.isEmpty(): + return "" # Find top root - i = self.bottom_root - while i.parent: - i = i.parent - # Print - self.__Print(i) - + top_root = self.bottom_root + while top_root.parent: + top_root = top_root.parent + heap_as_list = [] + self.__traversal(top_root, heap_as_list) + return "\n".join( + ("-" * level + str(value)) + for value, level in heap_as_list + ) -#%%% # Unit Tests - -# A random permutation of 30 integers to be inserted -import numpy as np - -permutation = np.random.permutation(list(range(30))) - - -TestHeap = BinomialHeap() - -# 30 inserts -for number in permutation: - TestHeap.insert(number) -# Printing Heap -TestHeap.NodePrint() - -# Deleting -for i in range(20): - print(TestHeap.deleteMin(), end=" ") # 0, 1, 2, 3, ... , 19 +if __name__ == "__main__": + # A random permutation of 30 integers to be inserted + import numpy as np + + permutation = np.random.permutation(list(range(30))) + # Create a Heap and insert + TestHeap = BinomialHeap() + + # 30 inserts + for number in permutation: + TestHeap.insert(number) + + # Printing Heap + print(TestHeap) + # Deleting + for i in range(20): + print( + TestHeap.deleteMin(), end=" " + ) # 0, 1, 2, 3, ... , 19 From af873648c29bf88d809d1c02b3bf01ea3f320dce Mon Sep 17 00:00:00 2001 From: KirilBangachev <51961981+KirilBangachev@users.noreply.github.com> Date: Wed, 4 Sep 2019 08:18:14 +0300 Subject: [PATCH 5/7] Update binomial_heap.py - Added more tests - Added to the documentation - Stylistic editing - mergeHeaps now also returns a reference to the merged heap - added a preOrder function that returns a list with the preorder of the heap --- data_structures/heap/binomial_heap.py | 123 +++++++++++++++++++------- 1 file changed, 92 insertions(+), 31 deletions(-) diff --git a/data_structures/heap/binomial_heap.py b/data_structures/heap/binomial_heap.py index 4c87e9c2103d..176673ec9d49 100644 --- a/data_structures/heap/binomial_heap.py +++ b/data_structures/heap/binomial_heap.py @@ -1,14 +1,15 @@ """ Min-oriented priority queue implemented with the Binomial Heap data - structure implemented with the BinomialHeap class. There is also a helper + structure implemented with the BinomialHeap class. + + There is also a helper class Node. Performance details: - Insert element in a heap with n elemnts: Guaranteed logn, amoratized 1 - Merge (meld) heaps of size m and n: O(logn + logm) - - Delete Min: O(logn) - - Inserting and merging performance are the main advantages over a binary heap + - Delete Min: O(logn) + - Peek (return min without deleting it): O(1) Reference: Advanced Data Structures, Peter Brass """ @@ -24,18 +25,17 @@ class Node: def __init__(self, val): self.val = val - self.left_tree_size = ( # Number of nodes in left subtree - 0 - ) + # Number of nodes in left subtree + self.left_tree_size = 0 self.left = None self.right = None self.parent = None def mergeTrees(self, other): """ - In-place merge of two binomial trees of equal size. - Returns the root of the resulting tree - """ + In-place merge of two binomial trees of equal size. + Returns the root of the resulting tree + """ assert ( self.left_tree_size == other.left_tree_size ), "Unequal Sizes of Blocks" @@ -71,6 +71,10 @@ def __init__( self.min_node = min_node def mergeHeaps(self, other): + """ + In-place merge of two binomial heaps. + Both of them become the resulting merged heap + """ # Empty heaps corner cases if other.size == 0: @@ -149,7 +153,18 @@ def mergeHeaps(self, other): i = i.left self.bottom_root = i + # Update other + other.size = self.size + other.bottom_root = self.bottom_root + other.min_node = self.min_node + + # Return the merged heap + return self + def insert(self, val): + """ + insert a value in the heap + """ if self.size == 0: self.bottom_root = Node(val) self.size = 1 @@ -191,13 +206,19 @@ def insert(self, val): next_node.left = self.bottom_root def peek(self): + """ + return min element without deleting it + """ return self.min_node.val def isEmpty(self): return self.size == 0 def deleteMin(self): - assert not self.isEmpty(), "Empty Heap" + """ + delete min element and return it + """ + # assert not self.isEmpty(), "Empty Heap" # Save minimal value min_value = self.min_node.val @@ -300,6 +321,20 @@ def deleteMin(self): return min_value + def preOrder(self): + """ + Returns the Pre-order representation of the heap including + values of nodes plus their level distance from the root; + Empty nodes appear as # + """ + # Find top root + top_root = self.bottom_root + while top_root.parent: + top_root = top_root.parent + heap_preOrder = [] + self.__traversal(top_root, heap_preOrder) + return heap_preOrder + def __traversal(self, curr_node, preorder, level=0): """ Pre-order traversal of nodes @@ -322,35 +357,61 @@ def __str__(self): """ if self.isEmpty(): return "" - # Find top root - top_root = self.bottom_root - while top_root.parent: - top_root = top_root.parent - heap_as_list = [] - self.__traversal(top_root, heap_as_list) + print(self.preOrder()) + preorder_heap = self.preOrder() + return "\n".join( ("-" * level + str(value)) - for value, level in heap_as_list + for value, level in preorder_heap ) # Unit Tests if __name__ == "__main__": - # A random permutation of 30 integers to be inserted + # A random permutation of 30 integers to be inserted and 19 of them deleted import numpy as np permutation = np.random.permutation(list(range(30))) - # Create a Heap and insert - TestHeap = BinomialHeap() - # 30 inserts + # Create a Heap and insert - __init__() test + first_heap = BinomialHeap() + + # 30 inserts - insert() test for number in permutation: - TestHeap.insert(number) - - # Printing Heap - print(TestHeap) - # Deleting - for i in range(20): - print( - TestHeap.deleteMin(), end=" " - ) # 0, 1, 2, 3, ... , 19 + first_heap.insert(number) + # size test + print(first_heap.size) # 30 + + # Deleting - delete() test + for i in range(25): + print(first_heap.deleteMin(), end=" ") + print() + # 0, 1, 2, 3, ... , 24 + + # Create a new Heap + second_heap = BinomialHeap() + vals = [17, 20, 31, 34] + for value in vals: + second_heap.insert(value) + """ + The heap should have the following structure: + + 17 + / \ + # 31 + / \ + 20 34 + / \ / \ + # # # # + """ + # preOrder() test + print(second_heap.preOrder()) + + # Printing Heap - __str__() test + print(second_heap) + + # mergeHeaps() test + second_heap.mergeHeaps(first_heap) + # preOrder of merged heap + print(first_heap.preOrder()) + # Should include the values 17, 20, 25, 26, 27, 28, 29, 31, 34 From 3c23f5a023273563b4d3596903a9d3c090ef05bd Mon Sep 17 00:00:00 2001 From: KirilBangachev <51961981+KirilBangachev@users.noreply.github.com> Date: Wed, 4 Sep 2019 21:22:38 +0300 Subject: [PATCH 6/7] Update binomial_heap.py Changed the unit tests structure --- data_structures/heap/binomial_heap.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/data_structures/heap/binomial_heap.py b/data_structures/heap/binomial_heap.py index 176673ec9d49..f8be13011052 100644 --- a/data_structures/heap/binomial_heap.py +++ b/data_structures/heap/binomial_heap.py @@ -357,7 +357,6 @@ def __str__(self): """ if self.isEmpty(): return "" - print(self.preOrder()) preorder_heap = self.preOrder() return "\n".join( @@ -366,8 +365,9 @@ def __str__(self): ) +#%% # Unit Tests -if __name__ == "__main__": +def main(): # A random permutation of 30 integers to be inserted and 19 of them deleted import numpy as np @@ -380,12 +380,15 @@ def __str__(self): for number in permutation: first_heap.insert(number) # size test + print("Size:") print(first_heap.size) # 30 + print() # Deleting - delete() test + print("First 25 elements:") for i in range(25): print(first_heap.deleteMin(), end=" ") - print() + print("\n\n") # 0, 1, 2, 3, ... , 24 # Create a new Heap @@ -405,13 +408,22 @@ def __str__(self): # # # # """ # preOrder() test + print("Pre-order of heap:") print(second_heap.preOrder()) + print() # Printing Heap - __str__() test + print("Heap: ") print(second_heap) + print() # mergeHeaps() test second_heap.mergeHeaps(first_heap) # preOrder of merged heap + print("Merged heap pre-oder") print(first_heap.preOrder()) # Should include the values 17, 20, 25, 26, 27, 28, 29, 31, 34 + + +if __name__ == "__main__": + main() From 903da33ef16361c09829c19094693de777b89946 Mon Sep 17 00:00:00 2001 From: KirilBangachev <51961981+KirilBangachev@users.noreply.github.com> Date: Thu, 5 Sep 2019 08:50:06 +0300 Subject: [PATCH 7/7] Turned the tests into doctests --- data_structures/heap/binomial_heap.py | 157 ++++++++++++++------------ 1 file changed, 85 insertions(+), 72 deletions(-) diff --git a/data_structures/heap/binomial_heap.py b/data_structures/heap/binomial_heap.py index f8be13011052..bc9cb5145f2e 100644 --- a/data_structures/heap/binomial_heap.py +++ b/data_structures/heap/binomial_heap.py @@ -1,15 +1,5 @@ """ - Min-oriented priority queue implemented with the Binomial Heap data - structure implemented with the BinomialHeap class. - - There is also a helper - class Node. - - Performance details: - - Insert element in a heap with n elemnts: Guaranteed logn, amoratized 1 - - Merge (meld) heaps of size m and n: O(logn + logm) - - Delete Min: O(logn) - - Peek (return min without deleting it): O(1) + Binomial Heap Reference: Advanced Data Structures, Peter Brass """ @@ -20,7 +10,7 @@ class Node: Node in a doubly-linked binomial tree, containing: - value - size of left subtree - - link to left, right and parent nodes + - link to left, right and parent nodes """ def __init__(self, val): @@ -63,6 +53,85 @@ def mergeTrees(self, other): class BinomialHeap: + """ + Min-oriented priority queue implemented with the Binomial Heap data + structure implemented with the BinomialHeap class. It supports: + + - Insert element in a heap with n elemnts: Guaranteed logn, amoratized 1 + - Merge (meld) heaps of size m and n: O(logn + logm) + - Delete Min: O(logn) + - Peek (return min without deleting it): O(1) + + Example: + + Create a random permutation of 30 integers to be inserted and + 19 of them deleted + >>> import numpy as np + >>> permutation = np.random.permutation(list(range(30))) + + Create a Heap and insert the 30 integers + + __init__() test + >>> first_heap = BinomialHeap() + + 30 inserts - insert() test + >>> for number in permutation: + ... first_heap.insert(number) + + Size test + >>> print(first_heap.size) + 30 + + Deleting - delete() test + >>> for i in range(25): + ... print(first_heap.deleteMin(), end=" ") + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 + + Create a new Heap + >>> second_heap = BinomialHeap() + >>> vals = [17, 20, 31, 34] + >>> for value in vals: + ... second_heap.insert(value) + + + The heap should have the following structure: + + 17 + / \ + # 31 + / \ + 20 34 + / \ / \ + # # # # + + preOrder() test + >>> print(second_heap.preOrder()) + [(17, 0), ('#', 1), (31, 1), (20, 2), ('#', 3), ('#', 3), (34, 2), ('#', 3), ('#', 3)] + + printing Heap - __str__() test + >>> print(second_heap) + 17 + -# + -31 + --20 + ---# + ---# + --34 + ---# + ---# + + mergeHeaps() test + >>> merged = second_heap.mergeHeaps(first_heap) + >>> merged.peek() + 17 + + values in merged heap; (merge is inplace) + >>> while not first_heap.isEmpty(): + ... print(first_heap.deleteMin(), end=" ") + 17 20 25 26 27 28 29 31 34 + + """ + def __init__( self, bottom_root=None, min_node=None, heap_size=0 ): @@ -331,6 +400,7 @@ def preOrder(self): top_root = self.bottom_root while top_root.parent: top_root = top_root.parent + # preorder heap_preOrder = [] self.__traversal(top_root, heap_preOrder) return heap_preOrder @@ -365,65 +435,8 @@ def __str__(self): ) -#%% # Unit Tests -def main(): - # A random permutation of 30 integers to be inserted and 19 of them deleted - import numpy as np - - permutation = np.random.permutation(list(range(30))) - - # Create a Heap and insert - __init__() test - first_heap = BinomialHeap() - - # 30 inserts - insert() test - for number in permutation: - first_heap.insert(number) - # size test - print("Size:") - print(first_heap.size) # 30 - print() - - # Deleting - delete() test - print("First 25 elements:") - for i in range(25): - print(first_heap.deleteMin(), end=" ") - print("\n\n") - # 0, 1, 2, 3, ... , 24 - - # Create a new Heap - second_heap = BinomialHeap() - vals = [17, 20, 31, 34] - for value in vals: - second_heap.insert(value) - """ - The heap should have the following structure: - - 17 - / \ - # 31 - / \ - 20 34 - / \ / \ - # # # # - """ - # preOrder() test - print("Pre-order of heap:") - print(second_heap.preOrder()) - print() - - # Printing Heap - __str__() test - print("Heap: ") - print(second_heap) - print() - - # mergeHeaps() test - second_heap.mergeHeaps(first_heap) - # preOrder of merged heap - print("Merged heap pre-oder") - print(first_heap.preOrder()) - # Should include the values 17, 20, 25, 26, 27, 28, 29, 31, 34 - - if __name__ == "__main__": - main() + import doctest + + doctest.testmod()