Repository where I mostly put random python scripts.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
3.8 KiB

  1. """
  2. :Author: James Sherratt
  3. :Date: 20/10/2019
  4. :License: MIT
  5. :name: heapsort.py
  6. Heap sorts a list-like object. Note: this has been written with code-clarity
  7. in mind first, efficiency second.
  8. """
  9. from random import randint
  10. def get_left(i):
  11. """
  12. Get the left element index of a heap node for an array.
  13. :param i: The parent index.
  14. :return: the left element.
  15. """
  16. return 2 * i + 1
  17. def get_right(i):
  18. """
  19. Get the right element index of a heap node for an array.
  20. :param i: The parent index.
  21. :return: the right element.
  22. """
  23. return 2 * i + 2
  24. def repair_heap(vals_list, root, arr_top):
  25. """
  26. Sifts the root element of a heap to the correct position, to
  27. correct a max heap. This assumes the children of the root node are max heaps.
  28. :param vals_list: list of values, which represents a heap structure.
  29. :param root: the index of the node we're working from/ using as a root.
  30. :param arr_top: the largest value of the list we're interested in.
  31. :return: Reference to the passed list, with the root node in the correct position.
  32. """
  33. # This is the value to swap. We want to swap the root value down, so we swap the root first.
  34. swap = root
  35. # Get left and right nodes of root.
  36. left = get_left(root)
  37. right = get_right(root)
  38. while left < arr_top:
  39. # Check if value to swap is less than the left child.
  40. if vals_list[swap] < vals_list[left]:
  41. swap = left
  42. # Check if value to swap is less than the right child (if exists).
  43. # Note: these 2 if's could be combined using "and", but then we're relying on lazy evaluation.
  44. if right < arr_top:
  45. if vals_list[swap] < vals_list[right]:
  46. swap = right
  47. # Check if the swap is still the root. If so, there's no more children to swap and we're done.
  48. if swap == root:
  49. return vals_list
  50. # Else, swap.
  51. else:
  52. vals_list[root], vals_list[swap] = vals_list[swap], vals_list[root]
  53. # New root, left and right node for the next iteration.
  54. root = swap
  55. left = get_left(root)
  56. right = get_right(root)
  57. return vals_list
  58. def max_heap(vals_list):
  59. """
  60. Convert a list of values into a max heap tree.
  61. :param vals_list: list of numbers.
  62. :return: the same list as a max heap tree.
  63. """
  64. # Create a max heap by repairing the heap, starting from the nodes one above the leaf nodes.
  65. len_list = len(vals_list)
  66. for root in range(len_list//2, -1, -1):
  67. repair_heap(vals_list, root, len_list)
  68. return vals_list
  69. def max_heap_to_sorted(vals_list):
  70. """
  71. Convert a max heap list into a sorted list.
  72. :param vals_list: list containing max heap.
  73. :return: the same list of values, sorted.
  74. """
  75. # i is the index of the last element of the slice of the array that needs sorting.
  76. for top in range(len(vals_list)-1, 0, -1):
  77. # Swap the root value (max) with the last value of the slice.
  78. vals_list[0], vals_list[top] = vals_list[top], vals_list[0]
  79. # Sift the new root to the correct position of the remainder of the max heap.
  80. # Another way of doing this is to pass a slice of the vals_list up to the value top, but python passes
  81. # slices by copy so there's a massive performance hit.
  82. repair_heap(vals_list, 0, top)
  83. return vals_list
  84. def heapsort(vals_list):
  85. """
  86. Sort a list of values using heapsort.
  87. :param vals_list: list of sortable values.
  88. :return: the same list, sorted.
  89. """
  90. max_heap(vals_list)
  91. return max_heap_to_sorted(vals_list)
  92. if __name__ == "__main__":
  93. list_len = 100000
  94. vals_list = [randint(0, (2**16)) for i in range(list_len)]
  95. heap_sorted = heapsort(list(vals_list))
  96. py_sorted = sorted(vals_list)
  97. print("Did the sort work? {}".format(heap_sorted == py_sorted))