Heap Sort
A Binary Heap is a Complete Binary Tree where the items are stored in a special order such that the value in a parent node is greater or smaller than the two values in the children nodes.
Heap Sort is a comparison-based sorting algorithm — somewhat similar to selection sort — which divides a list into sorted and unsorted parts, then it iteratively shrinks the unsorted part by extracting the largest value and moving that to the sorted part. The improvement consists of the usage of a heap data structure rather than a linear-time search to find the maximum.
Binary Tree Sort
A Binary Tree Sort is an algorithm that builds a binary search tree from the elements to be sorted, and then traverses the tree (in-order) so that the elements come out in sorted order.
Average Case Time Complexity : \$O(N\: log N)\$ adding one element to a Binary Search Tree on average takes \$O(log N)\$ time (height of a tree). Therefore, adding \$n\$ elements will take \$O(N\: log N)\$ time.
Worst Case Time Complexity : \$O(N^2)\$. The worst case can be improved by using a self-balancing binary search tree, which will take \$O(N\: log N)\$ time to sort the array in worst case.
Just for practicing OOP, I've implemented the above algorithms, and if you'd like to review the code and provide any change/improvement recommendations (especially I'm not still sure how to design exception handler for each class), please do so, and I appreciate that.
ideone.com Demo (Python)
Code
''' This module combines Tree Sorting Algorithms, just for practicing OOP in Python. It is an imaginary problem to solve assuming that we should use OOP to write these algorithms, even if that'd be unnecessary. Currently it has a __super__ class TreeSortingAlgorithms with two internal HeapSort and BinarySort classes, each with their own internal classes. I'm not sure how to design an OOP exception handling for each class or for the entire module. ''' # imports random for testing the algorithms import random # imports TypeVar and List typings for type hinting from typing import List, TypeVar # Instantiates the TypeVar T = TypeVar('T') class TreeSortingAlgorithms: '''This super class has two subclasses of HeapSort and BinarySort.''' class HeapSort: '''HeapSort gets an input list, uses iterative or recursive bottom-up approaches and sorts the input list. ''' class ExceptionHandling(Exception): '''This class is supposed to handle the exceptions for Heap Sort, not yet though''' pass def heap_sort(input_list: List[T], input_size: int) -> None: ''' Sorts a numerical list in an ascending order e.g., [None, 5, 3.1, 2, 9] to [None, 2, 3.1, 5, 9] Input list is a list which has a None first element at index 0 The zero index is just set to None for simplicity. ''' TreeSortingAlgorithms.HeapSort.buttom_up_heap_sort( input_list, input_size) while input_size > 1: # Assigns the max value of the heap max_value = input_list[1] # Swaps the max value with the last element value input_list[1], input_list[input_size] = input_list[input_size], max_value # Increments input_size -= 1 # Restores down the first element TreeSortingAlgorithms.HeapSort.restore_down( 1, input_list, input_size) def buttom_up_heap_sort(input_list: List[T], input_size: int) -> None: ''' Builds the heap sort using bottom up approach ''' heap_index = input_size // 2 while heap_index >= 1: # Restores down the element TreeSortingAlgorithms.HeapSort.restore_down( heap_index, input_list, input_size) # Increments heap_index -= 1 def restore_down(heap_index: int, input_list: List[T], input_size: int) -> None: ''' Restores down the element ''' # Assigns the item value heap_value = input_list[heap_index] # Assigns the left and right child indices left_child_index, right_child_index = 2 * heap_index, 2 * heap_index + 1 while right_child_index <= input_size: if heap_value >= input_list[right_child_index] and heap_value >= input_list[left_child_index]: input_list[heap_index] = heap_value return elif input_list[left_child_index] > input_list[right_child_index]: input_list[heap_index], heap_index = input_list[left_child_index], left_child_index else: input_list[heap_index], heap_index = input_list[right_child_index], right_child_index # Assigns the left and right child indices left_child_index, right_child_index = 2 * heap_index, 2 * heap_index + 1 if left_child_index == input_size and heap_value < input_list[left_child_index]: input_list[heap_index], heap_index = input_list[left_child_index], left_child_index input_list[heap_index] = heap_value class BinarySort: '''Binary Sort class has a class of Node, an ExceptionHandling class (to be written), and the following methods: is_empty: returns boolean if the tree is empty recursive_insert: recursively insert the new values in the tree iterative_insert: iteratively insert the new values in the tree''' def __init__(self) -> None: ''' Initializes the Root Node of the Binary Tree to None; ''' self.root: BinarySort = None class ExceptionHandling(Exception): '''This class is supposed to handle the exceptions for Binary Sort, not yet though''' pass class Node: '''This class instantiate the Node''' def __init__(self, node_value: float) -> None: ''' Initializes a node with three attributes; `node_value` (Node Value): Must be an float; `right_child` (Right Child): Initializes to `None`; Its value must be larger than the Root Node; `left_child` (Left Child): Initializes to `None`; Its value must be smaller than the Root Node; ''' self.node_value: Node = node_value self.right_child: Node = None self.left_child: Node = None def is_empty(self) -> bool: ''' Returns True if the tree doesn't have a node; ''' return self.root is None def recursive_insert(self, new_value: float) -> None: ''' Recursively inserts an float-value Node in the Tree; ''' self.root = self._recursive_insert(self.root, new_value) def _recursive_insert(self, parent_node: Node, new_value: float) -> Node: ''' Inserts an float value in the left or right Node; Returns the parent Node; ''' # If the parent node is none if parent_node is None: parent_node = TreeSortingAlgorithms.BinarySort.Node(new_value) # If the new Node value is smaller than its parent Node value elif new_value < parent_node.node_value: parent_node.left_child = self._recursive_insert( parent_node.left_child, new_value) # If the new Node value is bigger than its parent Node value else: parent_node.right_child = self._recursive_insert( parent_node.right_child, new_value) return parent_node def iterative_insert(self, new_value: float) -> None: '''Iteratively inserts a new value as a Node in the Binary Search Tree''' new_node = TreeSortingAlgorithms.BinarySort.Node(new_value) # If tree is not empty if TreeSortingAlgorithms.BinarySort().is_empty(): # Assigns the parent node to the Root parent_node = self.root while True: # If the new value is smaller than the parent node value if new_value < parent_node.node_value: # If parent node has a left child if parent_node.left_child: parent_node = parent_node.left_child # If parent node doesn't have a left child else: parent_node.left_child = new_node break # If the new value is not smaller than the parent node value else: # If parent node has a right child if parent_node.right_child: parent_node = parent_node.right_child else: # If parent node doesn't have a right child parent_node.right_child = new_node break return self.root = new_node def __iter__(self) -> None: ''' Calls the _inorder traversing method recursively; ''' self._inorder(self.root) def _inorder(self, parent_node: Node) -> None: ''' Traverses the tree inorder (Left Node, Root Node, Right Node) ''' if parent_node is None: return # Recurse the left node self._inorder(parent_node.left_child) # Prints the parent node print(parent_node.node_value) # Recurse the right node self._inorder(parent_node.right_child) if __name__ == '__main__': # ------------------------------------------------------------------------- # ---------------------------- Binary Sort Tests --------------------------- # ------------------------------------------------------------------------- tree = TreeSortingAlgorithms.BinarySort() delimiter = '-' * 50 # -------------------------- Recursive Test -------------------------- print('Recursive Binary Search Tree outputs: ') for i in range(random.randint(20, 30)): tree.recursive_insert(random.uniform(-10, 10)) tree.__iter__() # -------------------------- Iterative Test -------------------------- print('Iterative Binary Search Tree outputs: ') for i in range(random.randint(20, 30)): tree.iterative_insert(random.uniform(-10, 10)) tree.__iter__() # ------------------------------------------------------------------------- # ---------------------------- Heap Sort Tests --------------------------- # ------------------------------------------------------------------------- # Tests the heap sort with some randomly generated lists for test_heap_index in range(10): # Generates random input list size for testing input_size = random.randint(20, 50) # Assigns the first element of the input list to None input_list = [None] # Randomly generates the other elements of the input list # Also, assigns that to a second test list for comparison test_list = input_list[1:] = [random.randint(100, 1000) for _ in range(1, input_size + 1)] # Sorts the input list TreeSortingAlgorithms.HeapSort.heap_sort(input_list, input_size) # If heap sort list output is equal to Python built-in sort output if (sorted(test_list) == input_list[1:]): print(f'🍏 Heap Sort Test {test_heap_index + 1} was successful!') # Otherwise if the input list is not sorted else: print(f'🍎 Heap Sort Test {test_heap_index + 1} was not successful!')
Sources
The Binary Tree Sort section of this question has been also previously reviewed in the first following link: