Sorting
From quadratic insertion and selection sorts to O(n log n) merge and quick sort, and the comparison lower bound they all share.
Sorting is the canonical algorithmic problem: easy to state, rich in trade-offs, and a subroutine in countless other tasks from searching to merging. It is also the cleanest place to see asymptotic analysis pay off, because the difference between an and an sort is exactly what makes large data manageable.
Three families of idea
Quadratic sorts repeatedly find the next element by scanning; merge sort divides the problem in half and combines sorted halves; quicksort partitions around a pivot so that after one pass each side is nearer its final place. Ramanujan-style cleverness is not required — the wins come from dividing.
Run the same array through four sorts below and compare the work each one does.
Race the sorts on one array — compare the work each one does
Insertion sort is fast on nearly sorted data and O(n^2) when reversed; quicksort partitions around a pivot and averages O(n log n) but can hit its worst case on adversarial input. Selection sort always makes the same number of comparisons. Counts are for this one input, not a proof of the asymptotics.
The quadratic sorts
- Insertion sort takes each element and shifts it left into place. It is when the data is nearly sorted and when reversed, and it wins on small arrays because its constant factor is tiny.
- Selection sort finds the minimum of the remaining suffix and swaps it forward. It always performs comparisons regardless of the input.
- Bubble sort repeatedly swaps adjacent out-of-order pairs. It is mostly of pedagogical value, though it detects an already-sorted array in one pass.
The divide-and-conquer sorts
Merge sort splits the array in half, sorts each half recursively, then merges two sorted runs in linear time:
It is stable and predictable, at the cost of auxiliary space.
Quicksort partitions around a pivot so smaller elements go left and larger go right, then recurses on both sides. Partitioning is in-place and fast, giving average time; a poor pivot choice (for example, always the last element on already-sorted data) degrades it to . Randomised or median pivots make that unlikely.
Ask about stability before you sort records
A sort is stable if equal keys keep their original relative order. Merge
sort is stable; quicksort and selection sort are not. If you sort a list of
people by surname and later by age, stability decides whether each age group
stays alphabetised. Python's sorted is stable, so it preserves the previous
ordering of ties.
The comparison lower bound
Any sort that only compares pairs of elements needs at least comparisons in the worst case. There are possible orderings, and each comparison yields at most one bit, so a decision tree of depth can distinguish at most orderings: forces . Counting sort and radix sort beat this only by not comparing — they trade memory and key assumptions for linear time.
In practice, Python's sorted is Timsort: merge sort with insertion-sort runs,
detecting existing order and staying stable.
Illustrative vs real
The race runs on eight values and counts comparisons and swaps, so the bars and totals come from actual executions. Real inputs range from already-sorted to adversarial, and cache behaviour shapes the constant factor. The asymptotics and the stability distinction are what carry over unchanged.
Check yourself
Eduspheria wiki · Programming & Data Structures, Algorithms
0 / 5 answered
From the mid-term paper
Modeled on NITJ AI-507, Mid-Term October 2024
0 / 5 answered
Where next: searching — finding an element, and why sorted data changes the whole game.