Heapsort
Algorithm
Heapsort sorts an array in two phases:
Phase 1: Build a max-heap from the input array in time.
Phase 2: Repeatedly extract the maximum and place it at the end:
- Build-Max-Heap().
- For down to 2:
- Swap (the maximum) with .
- Decrement
heap_size(excludes the sorted suffix). - Max-Heapify(, 1).
After each iteration, is a max-heap and contains the largest elements in sorted order. When reaches , the entire array is sorted in ascending order (assuming a max-heap; for descending order, use a min-heap).
Walkthrough
Input:
Phase 1 (Build-Heap): $[1, 9, 3, 4, 2, 7, 6, 5, 8] → [9, 8, 7, 5, 2, 3, 6, 1, 4]$ (max-heap)
Phase 2 (Extract-Max loop):
i=9: swap 9 ⇄ 4, heapify → heap [8,5,7,1,2,3,6,4 | 9]
i=8: swap 8 ⇄ 4, heapify → heap [7,5,6,1,2,3,4 | 8, 9]
i=7: swap 7 ⇄ 4, heapify → heap [6,5,3,1,2,4 | 7, 8, 9]
i=6: swap 6 ⇄ 4, heapify → heap [5,4,3,1,2 | 6, 7, 8, 9]
i=5: swap 5 ⇄ 2, heapify → heap [4,2,3,1 | 5, 6, 7, 8, 9]
i=4: swap 4 ⇄ 1, heapify → heap [3,2,1 | 4, 5, 6, 7, 8, 9]
i=3: swap 3 ⇄ 1, heapify → heap [2,1 | 3, 4, 5, 6, 7, 8, 9]
i=2: swap 2 ⇄ 1, heapify → heap [1 | 2, 3, 4, 5, 6, 7, 8, 9]
Final: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Complexity Analysis
Phase 1 (Build-Max-Heap): as proved earlier.
Phase 2: iterations, each calling Max-Heapify on the root of a shrinking heap. Max-Heapify on a heap of size costs . Summing:
Using Stirling’s approximation: . Therefore:
This bound is worst-case. Heapsort never degrades to , unlike Quicksort.
In-Place Property
Heapsort uses only extra space beyond the input array (a few variables for indices and the temporary for swaps). The heap is stored within the same array as the sorted output, separated by the heap_size boundary. This is achieved by using the suffix of the array for the sorted region and the prefix for the heap.
Cache Performance
Heapsort’s in-place nature comes at a cost: Max-Heapify jumps between array positions at powers of 2 (), which are far apart in memory for large indices. This results in poor cache locality compared to Quicksort (which scans contiguously during partitioning) and MergeSort (which merges contiguously). In practice, Heapsort is often slower than Quicksort for moderate , despite its superior worst-case guarantee.
Comparison with Other Sorts
| Sort | Worst Case | Average Case | In-place | Stable |
|---|---|---|---|---|
| Heapsort | Yes | No | ||
| MergeSort | No () | Yes | ||
| Quicksort | Yes | No | ||
| Insertion Sort | Yes | Yes |
Summary
| Phase | Cost | Description |
|---|---|---|
| Build-Heap | Bottom-up max-heapify | |
| Extract loop | extractions, each | |
| Total | Worst-case guarantee | |
| Space | In-place via array prefix/suffix split | |
| Stable | No | Swaps non-adjacent elements |
| Cache | Poor | Non-local memory access pattern |
| Advantage | Good worst case, in-place | |
| Disadvantage | Slower constant factors, poor cache |