Heap Sort
Heap Sort
Category
Sorting
Difficulty
Hard
Problem Statement
Implement the heap sort algorithm to sort an array of integers in ascending order. Heap sort leverages a binary heap data structure to efficiently extract elements in sorted order.
Intuition
A max-heap guarantees that the largest element is always at the root. By building a max-heap from the input array, we can repeatedly extract the maximum element and place it at the end of the array. After each extraction, we restore the heap property on the reduced heap. This produces a sorted array in ascending order.
Approach
- Build a max-heap from the input array by calling sift-down on every non-leaf node, starting from the last non-leaf and working up to the root.
- Extract elements one at a time:
- Swap the root (maximum) with the last element of the heap.
- Reduce the heap size by one.
- Sift down the new root to restore the max-heap property.
- Repeat until the heap size is 1.
Pseudocode
function heapSort(array):
// Build max-heap
lastParent = (length(array) - 2) / 2 // integer division
for i from lastParent down to 0:
siftDown(array, i, length(array) - 1)
// Extract elements
for endIdx from length(array) - 1 down to 1:
swap(array[0], array[endIdx])
siftDown(array, 0, endIdx - 1)
return array
function siftDown(array, currentIdx, endIdx):
childOneIdx = 2 * currentIdx + 1
while childOneIdx <= endIdx:
childTwoIdx = 2 * currentIdx + 2 if 2 * currentIdx + 2 <= endIdx else -1
if childTwoIdx != -1 and array[childTwoIdx] > array[childOneIdx]:
idxToSwap = childTwoIdx
else:
idxToSwap = childOneIdx
if array[idxToSwap] > array[currentIdx]:
swap(array[currentIdx], array[idxToSwap])
currentIdx = idxToSwap
childOneIdx = 2 * currentIdx + 1
else:
break
Time & Space Complexity
- Time: O(n log n) in all cases (best, average, worst). Building the heap is O(n). Each of the n extractions requires O(log n) sift-down operations.
- Space: O(1) — Sorting is done entirely in-place. No auxiliary array or recursion stack is needed (sift-down is iterative).
Key Insights
- Heap sort has guaranteed O(n log n) worst-case time, unlike quick sort.
- Building the heap via sift-down is O(n), not O(n log n), because most nodes are near the bottom and require few sift operations.
- Heap sort is not stable — the extraction step changes the relative order of equal elements.
- It has poor cache performance compared to quick sort because it accesses memory in a non-sequential pattern (parent-child jumps).
- O(1) space makes heap sort ideal when memory is constrained.
Edge Cases
- Empty array — nothing to sort.
- Single-element array — already sorted.
- Already sorted array — still O(n log n); the heap build rearranges elements.
- All identical elements — sift-down terminates immediately at each step, but overall still O(n log n) comparisons.
- Two elements — one comparison and at most one swap.