An Implementation of Heapsort in Python.

from typing import List, TypeVar, Generic, Tuple
from collections.abc import Callable
from math import floor

T = TypeVar('T')
Comparator = Callable[[id:T,T], int]

def get_parent_index(child_index: int) -> int:
    if child_index == 0:
        return 0

    return floor((child_index - 1) / 2.0)

def get_child_indices(parent_index: int) -> Tuple[int, int]:
    return (parent_index * 2) + 1, (parent_index * 2) + 2

def heapsort(items: List[T], comparator: Comparator) -> List[T]:
    """Sort ITEMS in-place using heapsort."""
    def heapify(heap_range: Tuple[int, int]) -> None:
        # TODO: Validate indices

        heap_start, heap_end = heap_range
        for i in range(heap_start + 1, heap_end + 1):
            heapify_up((heap_start, i))

    def heapify_up(heap_range: Tuple[int, int]) -> None:
        # TODO: Validate indices

        _, heap_end = heap_range
        child_index = heap_end
        parent_index = get_parent_index(child_index)
        while True:
            if child_index == parent_index:
                break

            if comparator(items[child_index], items[parent_index]) <= 0:
                break

            swap(parent_index, child_index)
            child_index = parent_index
            parent_index = get_parent_index(child_index)

    def heapify_down(heap_range: Tuple[int, int]) -> None:
        # TODO: Validate indices

        heap_start, heap_end = heap_range
        parent_index = heap_start
        while True:
            child_a_index, child_b_index = get_child_indices(parent_index)
            swap_index = parent_index

            if child_a_index <= heap_end and comparator(items[child_a_index], items[parent_index]) > 0:
                swap_index = child_a_index

            if child_b_index <= heap_end and comparator(items[child_b_index], items[parent_index]) > 0 and comparator(items[child_b_index], items[child_a_index]) > 0:
                swap_index = child_b_index

            if swap_index == parent_index:
                break

            swap(swap_index, parent_index)
            parent_index = swap_index


    def swap(index_a: int, index_b: int) -> None:
        # TODO: Validate indices
        value_a = items[index_a]
        items[index_a] = items[index_b]
        items[index_b] = value_a

    heapify((0, len(items)-1))
    for heap_end in reversed(range(1, len(items))):
        swap(0, heap_end)
        heapify_down((0, heap_end-1))

    return items

print(heapsort([4,2,5,19,1], lambda x,y: 1 if x > y else -1))
print(heapsort([4,2,5,19,1], lambda x,y: 1 if x < y else -1))

# Test get_parent_index
#print("Pass" if get_parent_index(1) == 0 else "Fail")
#print("Pass" if get_parent_index(2) == 0 else "Fail")
#print("Pass" if get_parent_index(3) == 1 else "Fail")
#print("Pass" if get_parent_index(4) == 1 else "Fail")
#print("Pass" if get_parent_index(5) == 2 else "Fail")
#print("Pass" if get_parent_index(0) == 0 else "Fail")

# Test get_child_indices
#print("Pass" if get_child_indices(0) == (1,2) else ("Fail", get_child_indices(0)))
#print("Pass" if get_child_indices(1) == (3,4) else ("Fail", get_child_indices(1)))
#print("Pass" if get_child_indices(2) == (5,6) else ("Fail", get_child_indices(1)))

Bibliography