Basic Algorithms — Heapsort. Sorting an array with “Heap” data... | by Keita Miyaki | Towards Data Science

In previous posts, we looked at different sorting algorithms such as Merge Sort and Quicksort, and this time another sorting algorithm, Heapsort, is discussed. Heapsort, as shown in its name, uses “Heap” data structure.
Heap is a data structure, and heaps are arrays of binary trees. Each node of the binary tree corresponds to one element of the array. As one node has zero, one, or two children nodes, for i-th element of the array, 2i-th and (2i+1)-th elements are its left and right children respectively.
The following figure depicts an example heap in which the numbers in nodes equal to the orders in a 12-element array. The root is the first element in the array, and its left and right children are the second and third elements respectively. The sixth element has only one left child, and the seventh has none.
There are two kinds of heaps: min-heap and max-heap. In min-heap parents nodes are smaller than children nodes (the root node is smallest), while in max-heap it is opposite (the root node is largest). We will use max-heap property for Heapsort in this article.
The strategy is as follows; i) turn an array into a max-heap; ii) pick the root, which is the maximum number; iii) keep the remaining array as a max heap; iv) recurse ii) and iii).
Here is the code for the step iii) which is also used in the step i). This “Max-Heapify” function takes two inputs: an array and an integer. The function compare the node at a given order (input integer) with its two children. If the node is smaller than either of the children, it is swapped with the largest of two children nodes.
def max_heapify(array, i):    l = (i+1) * 2 - 1    r = (i+1) * 2    length = len(array)    if l < length and array[l] > array[i]:        largest = l    else:         largest = i        if r < length and array[r] > array[largest]:        largest = r            if largest != i:        largest_value = array[largest]        array[largest] = array[i]        array[i] = largest_value        max_heapify(array, largest)            return array
While Max-Heapify does not exhaust the entire heap, by operating the function to all the node except for leaves (the end nodes of the heap) we can turn any array to a max-heap.
def build_max_heap(array):    length = len(array)    for i in range(length//2, -1, -1):        max_heapify(array, i)    return array
Now we are ready to implement Heapsort.
def heap_sort(array):    length = len(array)    array = build_max_heap(array)    for i in range(length-1, 0, -1):        largest = array[0]        array[0] = array[i]        array[i] = largest        max_heapify(array[:i], 0)            return array
The following figure explains how Heapsort processes a 12-element array; i) firstly we turn the array into a max-heap; ii) take the root, 12, and replace it with the last element, 3; iii) process Max-Heapify onthe remaining 11-element array at the root, and the nodes affected are shown in dark blue; iii) recurse ii) and iii). In the end we will obtain an array sorted by ascending order. Heapsort operates in-place, storing only constant amount of data outside of the input array.
We can analyze the cost of Heapsort by examining sub-functions of Max-Heapify and Build-Max-Heap.
The cost of Max-Heapify is O(lgn). Comparing a node and its two children nodes costs Θ(1), and in the worst case, we recurse ⌊log2n⌋ times to the bottom. Alternatively, the cost of Max-Heapify can be expressed with the height h of the heap O(h).
The cost of Build-Max-Heap is O(n). As an n-element heap has a depth of ⌊lgn⌋ and at any height, there are ⌈n/2h+ 1⌉ nodes except the leaves, we can derive the cost as a total of Max-Heapify costs O(h) as shown on the left.
The total cost of Heapsort is O(nlgn), as it calls Build-Max-Heap (O(n)) and Max-Heapify (O(lgn)) over (n-1) times.
The following chart describes different sorting algorithms. As expected, Heapsort follows other O(nlgn)-cost algorithms, while it is not as inexpensive as Quicksort with a higher constant hidden in O notation.
Although Heapsort does not beat Quicksort as a sorting algorithm, Heap as the data structure offers many different usages, and one of the most notable would be in priority queues. With this Heapsort as an introduction to Heaps, we will see how Heaps are applied to an efficient priority queue algorithm later.