Putting Things in Order
Sorting arranges a collection into order, and it is one of the most studied problems in computing because so much else (searching, deduplication, grouping) becomes easy once data is sorted.
The simple sorts are quadratic, , and easy to understand:
- Bubble sort repeatedly swaps adjacent out-of-order elements, letting large values "bubble" to the end.
- Selection sort repeatedly finds the smallest remaining element and places it next.
- Insertion sort builds a sorted prefix, inserting each new element into its correct place — and is genuinely fast on nearly-sorted or small inputs.
The efficient sorts are and are what real systems use for large data:
- Merge sort recursively splits the list in half, sorts each half, and merges the two sorted halves — a clean divide-and-conquer, always , and stable.
- Quicksort partitions around a pivot (smaller elements left, larger right) and recurses; it is on average and very fast in practice, though a bad pivot gives an worst case.
- Heapsort builds a heap and repeatedly extracts the extreme, guaranteeing .
A key property is stability: a stable sort preserves the relative order of elements that compare equal. This matters when sorting by one field while wanting ties broken by a previous order (sort by last name, keeping first-name order within ties). Merge sort is stable; the usual quicksort is not.
There is a proven lower bound: any comparison-based sort needs at least comparisons in the worst case — so is the best a general comparison sort can do.
Common pitfall: assuming the "best" sort is always the fastest asymptotic one. For small or nearly-sorted inputs, a simple insertion sort can beat an sort because of lower constant overhead — which is why real library sorts switch to insertion sort for small sub-arrays. Match the sort to the input.
A merge-sort recursion tree: a list splitting into halves down log n levels, then accent merge-arrows combining sorted runs back up, with a side note that each of the log n levels does O(n) work.