Heap Sort
O(n log n) SpaceO(1) Imagine a tournament bracket. The best player rises to the top. You remove the champion, let the remaining players re-compete (the next best rises), and repeat. After all rounds, you have players ranked from best to worst.
When to use it: You need guaranteed O(n log n) with O(1) extra space. Unlike merge sort (needs O(n) space) or quick sort (has O(n^2) worst case), heap sort gives you both guarantees.
Key insight: A max-heap lets you extract the largest element in O(log n). Do this n times and you have a sorted array — all in-place by swapping the root with the last unsorted element.
Common trap: Heap sort is not stable — equal elements may change their relative order. If stability matters, use merge sort instead.
function heapSort(arr):
n = length(arr)
# Build max-heap (start from last non-leaf node)
for i = n/2 - 1 down to 0:
heapify(arr, n, i)
# Extract elements one by one
for i = n - 1 down to 1:
swap(arr[0], arr[i]) # move current max to end
heapify(arr, i, 0) # restore heap on reduced array
function heapify(arr, heapSize, root):
largest = root
left = 2 * root + 1
right = 2 * root + 2
if left < heapSize and arr[left] > arr[largest]:
largest = left
if right < heapSize and arr[right] > arr[largest]:
largest = right
if largest != root:
swap(arr[root], arr[largest])
heapify(arr, heapSize, largest) # recurse on affected subtree def heap_sort(arr: list[int]) -> None:
"""Sort array in-place using heap sort."""
n = len(arr)
# Build max-heap
for i in range(n // 2 - 1, -1, -1):
heapify(arr, n, i)
# Extract elements
for i in range(n - 1, 0, -1):
arr[0], arr[i] = arr[i], arr[0]
heapify(arr, i, 0)
def heapify(arr: list[int], heap_size: int, root: int) -> None:
largest = root
left = 2 * root + 1
right = 2 * root + 2
if left < heap_size and arr[left] > arr[largest]:
largest = left
if right < heap_size and arr[right] > arr[largest]:
largest = right
if largest != root:
arr[root], arr[largest] = arr[largest], arr[root]
heapify(arr, heap_size, largest) Problem: Sort an array in-place with guaranteed O(n log n) worst-case time and no extra memory allocation.
How would you approach this?
Answer: This is heap sort because it provides O(n log n) worst-case time (unlike quick sort) and O(1) extra space (unlike merge sort). Build a max-heap, then repeatedly swap the root with the last element and re-heapify.
Problem: Find the k largest elements in an unsorted array.
How would you approach this?
Answer: Use a min-heap of size k. Iterate through the array, pushing each element onto the heap. If the heap exceeds size k, pop the minimum. At the end, the heap contains the k largest elements. Time: O(n log k).
Time: O(n log n) in all cases. Building the heap is O(n) (not O(n log n) — each heapify is proportional to node depth, and most nodes are near the bottom). Extracting all n elements costs O(n log n) since each extraction requires O(log n) heapify.
Space: O(1) because the heap is built in-place within the input array. The only extra memory is a few index variables. Recursive heapify uses O(log n) stack space, but an iterative version achieves true O(1).