Selection by median of medians
Find the k-th smallest item in linear time even in the worst case: the pivot is the median of the medians of groups of five.
Quickselect is linear on average, but with bad pivots it reaches O(n²). The median of medians (Blum, Floyd,
Pratt, Rivest and Tarjan, 1973) picks a pivot that is always good:
- Time:
O(n) - Space:
O(n) - CLRS, section 9.3
- Horowitz–Sahni, chapter 3
Seventeen numbers; we want the 4th smallest. The first pivot is 12, the answer lies in the smaller part, and the second pivot, 5, is the answer.
def select(a, k):# The k-th smallest item (k = 1 is the minimum), in worst-case linear time.if len(a) <= 5:a = sorted(a)return a[k - 1]groups = [sorted(a[i:i + 5]) for i in range(0, len(a), 5)]medians = [g[(len(g) - 1) // 2] for g in groups]pivot = select(medians, (len(medians) + 1) // 2)low = [x for x in a if x < pivot]high = [x for x in a if x > pivot]same = len(a) - len(low) - len(high)if k <= len(low):return select(low, k)if k <= len(low) + same:return pivotreturn select(high, k - len(low) - same)
Start
Recursion tree
Filled orange = running call · Orange outline = still on the stack · Grey = finished
Local variables
Orange = changed in this step
a list
Call stack
- select()line 1
Explanation
The idea
Quickselect is linear on average, but with bad pivots it reaches O(n²). The median of medians (Blum, Floyd,
Pratt, Rivest and Tarjan, 1973) picks a pivot that is always good:
- Split the array into groups of 5 and find the median of each group.
- Recursively find the median of those medians: that is the pivot.
- Split the array into smaller than, equal to and larger than the pivot, and continue only in the part that holds the answer.
Why linear?
At least half the groups have a median ≥ the pivot, and each of those has at least 3 items ≥ the pivot; so about
3n/10 items are larger and 3n/10 smaller than the pivot, and each part has at most 7n/10 items:
T(n) ≤ T(n/5) + T(7n/10) + O(n) ⇒ T(n) = O(n)
because 1/5 + 7/10 < 1.
What to watch in the trace
- The bars are the current call's array; watch how much smaller it gets each time.
- The call tree has two kinds of calls: one for the medians and one for the part that holds the answer.