Heapsort, mergesort, quicksort, binary search, and divide-and-conquer. The O(n log n) barrier and how the great algorithms reach it.
Sorting is the most fundamental algorithmic problem in computer science. Not because sorting itself is so exciting, but because so many other problems become easy once the data is sorted.
Need to find duplicates? Sort the data -- duplicates become neighbors. Need the median? Sort the data -- it's the middle element. Need to find the closest pair of numbers? Sort first -- the answer must be adjacent elements.
Consider the problem of testing whether two sets are disjoint (share no elements). Without sorting, you must compare every element of set A against every element of set B: O(nm) time. But if you sort the smaller set first, you can binary-search for each element of the larger set: O((n+m) log m) time. For large inputs, that's the difference between hours and seconds.
The library sort function in your programming language is one of the most useful tools you have. Use it freely. But understanding how the major sorting algorithms work teaches design techniques that apply far beyond sorting.
The simplest sorting algorithm is selection sort: repeatedly find the minimum element from the unsorted portion and put it next in the sorted output.
Selection sort does n iterations. On each iteration, it scans the remaining unsorted elements to find the minimum. The first scan looks at n elements, the second at n-1, then n-2, and so on. The total number of comparisons is:
Can we do better? The bottleneck is finding the minimum -- it takes O(n) per iteration because we use an unsorted array. But Chapter 3 gave us a data structure that finds the minimum in O(log n) time: the heap. Replacing the unsorted array with a heap immediately improves selection sort from O(n2) to O(n log n). That improved algorithm has a name: heapsort.
A heap is a binary tree stored in an array where every parent dominates its children (smaller in a min-heap, larger in a max-heap). The key property: the minimum is always at the root, accessible in O(1).
The array trick is elegant. For element at position k:
Heapsort works in two phases. First, build a heap from the unsorted data (O(n) time using bottom-up construction). Then, repeatedly extract the minimum (O(log n) each), producing elements in sorted order.
Click "Extract Min" to remove the root and watch the heap repair itself by bubbling down. The sorted output grows on the right.
Total cost: O(n) to build the heap + O(n log n) for n extractions = O(n log n). Heapsort is guaranteed O(n log n) in the worst case, with no extra memory beyond the array itself.
Mergesort is the classic divide-and-conquer sorting algorithm. The idea is beautifully simple:
The merge step is the key. Given two sorted arrays, we can merge them into one sorted array in O(n) time by repeatedly taking the smaller of the two front elements.
How many levels of recursion are there? Each level halves the array, so there are log2 n levels. Each level does O(n) total work (merging). Therefore the total is:
Watch the array split, sort recursively, and merge back together. Each row shows a level of the recursion.
Quicksort is the most widely used sorting algorithm in practice. Like mergesort it divides and conquers, but with a twist: the hard work happens during the divide, not the merge.
After partitioning, the pivot is in its final sorted position. No merge step is needed -- the array is sorted in place.
Despite the O(n2) worst case, quicksort is typically 2-3x faster than mergesort in practice. Why? The inner loop (partition step) has excellent cache locality, fewer data movements, and a very small constant factor. That's why most library sort functions use quicksort (or a hybrid like introsort).
Binary search is the key algorithm that makes sorted data powerful. Given a sorted array, we can find any element in O(log n) time by repeatedly halving the search space.
Each comparison eliminates half the remaining elements. After k comparisons, only n/2k elements remain. We're done when n/2k = 1, which gives k = log2 n.
Watch binary search find the target value. Each step highlights the remaining search space and the comparison.
Heapsort, mergesort, and quicksort all achieve O(n log n). Is this optimal, or could a sorting algorithm do better?
The answer is: no comparison-based sorting algorithm can do better than O(n log n). Here's the argument. A comparison sort can only learn about the order of elements by comparing pairs. With n elements, there are n! possible orderings. Each comparison has two outcomes (yes/no), so after k comparisons we can distinguish at most 2k orderings. To distinguish all n! orderings, we need:
This is an information-theoretic lower bound. No cleverness in algorithm design can circumvent it, because the information simply isn't there in fewer comparisons.
| Algorithm | Best | Average | Worst | Space | Stable? |
|---|---|---|---|---|---|
| Selection Sort | O(n2) | O(n2) | O(n2) | O(1) | No |
| Heapsort | O(n log n) | O(n log n) | O(n log n) | O(1) | No |
| Mergesort | O(n log n) | O(n log n) | O(n log n) | O(n) | Yes |
| Quicksort | O(n log n) | O(n log n) | O(n2) | O(log n) | No |
Let's watch three sorting algorithms compete in real time. Each algorithm sorts the same random array while you watch the comparisons and swaps happen.
Click "Race!" to sort a random array with three algorithms simultaneously. Watch the bar heights converge to sorted order. The counter shows comparisons made.
Sorting and searching are foundational tools that appear throughout the rest of the book. Here is where they connect:
| Concept | Where It Leads |
|---|---|
| Binary search | Used in Chapter 6 for edge selection, Chapter 9 for decision problems |
| Heaps | Chapter 6: Priority queues power Prim's and Dijkstra's algorithms |
| Divide-and-conquer | The pattern behind mergesort reappears in algorithms for closest pair, matrix multiplication, and FFT |
| Randomization | Quicksort's random pivot idea extends to randomized algorithms throughout Chapter 7 |
| The n log n barrier | Chapter 9: Lower bounds and impossibility results via reduction |