|
|
- """
- :Author: James Sherratt
- :Date: 20/10/2019
- :License: MIT
-
- :name: heapsort.py
-
- Heap sorts a list-like object. Note: this has been written with code-clarity
- in mind first, efficiency second.
- """
-
- from random import randint
-
-
- def get_left(i):
- """
- Get the left element index of a heap node for an array.
- :param i: The parent index.
- :return: the left element.
- """
- return 2 * i + 1
-
-
- def get_right(i):
- """
- Get the right element index of a heap node for an array.
- :param i: The parent index.
- :return: the right element.
- """
- return 2 * i + 2
-
-
- def repair_heap(vals_list, root, arr_top):
- """
- Sifts the root element of a heap to the correct position, to
- correct a max heap. This assumes the children of the root node are max heaps.
-
- :param vals_list: list of values, which represents a heap structure.
- :param root: the index of the node we're working from/ using as a root.
- :param arr_top: the largest value of the list we're interested in.
- :return: Reference to the passed list, with the root node in the correct position.
- """
- # This is the value to swap. We want to swap the root value down, so we swap the root first.
- swap = root
-
- # Get left and right nodes of root.
- left = get_left(root)
- right = get_right(root)
- while left < arr_top:
- # Check if value to swap is less than the left child.
- if vals_list[swap] < vals_list[left]:
- swap = left
- # Check if value to swap is less than the right child (if exists).
- # Note: these 2 if's could be combined using "and", but then we're relying on lazy evaluation.
- if right < arr_top:
- if vals_list[swap] < vals_list[right]:
- swap = right
- # Check if the swap is still the root. If so, there's no more children to swap and we're done.
- if swap == root:
- return vals_list
- # Else, swap.
- else:
- vals_list[root], vals_list[swap] = vals_list[swap], vals_list[root]
- # New root, left and right node for the next iteration.
- root = swap
- left = get_left(root)
- right = get_right(root)
- return vals_list
-
-
- def max_heap(vals_list):
- """
- Convert a list of values into a max heap tree.
-
- :param vals_list: list of numbers.
- :return: the same list as a max heap tree.
- """
- # Create a max heap by repairing the heap, starting from the nodes one above the leaf nodes.
- len_list = len(vals_list)
- for root in range(len_list//2, -1, -1):
- repair_heap(vals_list, root, len_list)
-
- return vals_list
-
-
- def max_heap_to_sorted(vals_list):
- """
- Convert a max heap list into a sorted list.
-
- :param vals_list: list containing max heap.
- :return: the same list of values, sorted.
- """
- # i is the index of the last element of the slice of the array that needs sorting.
- for top in range(len(vals_list)-1, 0, -1):
- # Swap the root value (max) with the last value of the slice.
- vals_list[0], vals_list[top] = vals_list[top], vals_list[0]
- # Sift the new root to the correct position of the remainder of the max heap.
- # Another way of doing this is to pass a slice of the vals_list up to the value top, but python passes
- # slices by copy so there's a massive performance hit.
- repair_heap(vals_list, 0, top)
-
- return vals_list
-
-
- def heapsort(vals_list):
- """
- Sort a list of values using heapsort.
-
- :param vals_list: list of sortable values.
- :return: the same list, sorted.
- """
- max_heap(vals_list)
- return max_heap_to_sorted(vals_list)
-
-
- if __name__ == "__main__":
- list_len = 100000
- vals_list = [randint(0, (2**16)) for i in range(list_len)]
- heap_sorted = heapsort(list(vals_list))
- py_sorted = sorted(vals_list)
- print("Did the sort work? {}".format(heap_sorted == py_sorted))
|