3.6.10 · D5Sorting & Searching

Question bank — Linear time selection — median of medians algorithm

1,820 words8 min readBack to topic

Before we start, a shared vocabulary so no symbol appears unearned:


True or false — justify

TF — Median of Medians finds the median of the array.
False. It finds a pivot that is merely "near-central"; the actual median is found by the outer Select recursion, which uses only to shrink the search. See Quickselect.
TF — MoM is asymptotically faster than randomized Quickselect.
False in the sense that matters: both are for the value you get. MoM's bound is worst-case, Quickselect's is expected — same asymptotic per call, but MoM has a larger constant factor.
TF — Any odd group size gives linear time.
False in general, but there is a clean criterion: for an odd group size , half the medians and elements per group are , giving surviving fraction discarded per side; linearity needs , which simplifies to . So fails, and every odd (5, 7, 9, …) works.
TF — Doubling the group size always helps.
False. Larger groups make the per-group median cost grow and shrink the term, but the constant for finding each median rises. Size 5 is the sweet spot in practice, not the largest legal size.
TF — The bound counts elements strictly less than .
False — it counts elements (the group medians themselves are included). The strict-vs-nonstrict distinction is why the 3-way split exists to handle ties.
TF — If we skip the recursive call and just take the median of the medians list directly, we still get .
False. Finding the median of items is itself a selection problem; done naively (e.g. by sorting) it costs , breaking linearity. The term must be a real recursive Select.
TF — Because , MoM recursion tree has depth like Merge Sort.
False. The two children have different sizes, so it isn't a balanced tree. Linearity comes from the geometric shrinkage of total work per level (see Master Theorem doesn't directly apply — this needs the substitution method), not from depth.
TF — MoM guarantees the pivot lands exactly in the middle.
False. It guarantees at least elements on each side, so the pivot's rank lies between and — a wide but bounded band, which is all linearity needs.

Spot the error

Error — "Groups of 5 mean at least elements are ."
The count is per-group medians, not all elements. Only ~half the groups have median , and only 3 of the 5 in each count, giving — not .
Error — "The recurrence is because elements survive."
The surviving (larger) side has at most elements, so the term is . The is what we discard guarantee, not what we recurse on.
Error — "Since , each level does the work, so total is — that's why it's ."
The conclusion () is right but the reasoning is loose: the two subproblems aren't at the same tree level, so you can't treat as a single per-level ratio. The correct argument is substituting and solving .
Error — "Group size 3 works if we count 2 elements per group instead of 3."
With groups of 3, the median plus 1 smaller element = 2 per group over half the groups gives guaranteed per side, so surviving side , and — still fails. No counting trick fixes size 3.
Error — "We use a 2-way partition () like standard Lomuto/Hoare; the list is wasteful."
With many duplicates of , a 2-way split can send an block into one side that fails to shrink, risking non-termination or . The bucket lets us answer instantly when falls in the equal range.
Error — "MoM is randomized, so it's a randomized algorithm with expected ."
MoM is fully deterministic — no coin flips. Its is worst-case, which is precisely the property that distinguishes it from randomized Quickselect.

Why questions

Why 5 and not 4?
4 is even, so a group of 4 has no single middle element — the median is ambiguous, and picking either middle weakens the "3 elements per group" count. Odd sizes give a clean single median; 5 is the smallest odd size whose fractions sum below 1.
Why must the sum of the two recursive fractions be strictly less than 1?
If they summed to exactly 1 (or more), substituting leaves no slack to absorb the term — you'd need , impossible. The strict gap is exactly what pays for the linear work.
Why does MoM appear in worst-case-optimal Quicksort?
Using as Quicksort's pivot guarantees each partition splits at least / , bounding depth at and total cost at worst-case — killing Quicksort's adversary.
Why is MoM rarely used in real code despite being linear?
Its constant factor is large (two recursive calls per level plus grouping overhead), so introselect or randomized Quickselect beats it on typical inputs. MoM is a guarantee and a building block, not a speed champion.
Why does sorting each group of 5 with a general sort NOT break linearity?
Because a group of 5 is constant size — sorting it costs a fixed number of comparisons no matter how big is — so all groups together cost . The trap is imagining the per-group cost grows with ; it does not, because the group size is a constant.
Why is the recurrence solved by substitution rather than the Master Theorem?
The classic Master Theorem is stated for the form — one subproblem size repeated times. MoM's two children have different sizes ( and ), so that classic form doesn't apply; we instead guess and verify (see Recurrence relations).

Edge cases

Edge — array of size .
The recursion bottoms out: sort the elements directly and index position . This constant-size base case is what stops the recursion from spinning forever.
Edge — not a multiple of 5 (last group has 1–4 elements).
Write with ; there are groups, one possibly short. Even after rounding, at least full groups sit on 's small side, each contributing 3 elements, so the count is — the from rounding is absorbed by the constant . Correctness is unaffected; only the constant shifts slightly.
Edge — all elements are equal.
Every group median is that value, equals it, so and are empty and holds everything. The code answers in one step via the branch — no infinite recursion, thanks to the 3-way split.
Edge — (minimum) or (maximum).
MoM still routes through the pivot: it recurses toward the correct extreme, discarding a constant fraction each call. It does not short-circuit to a linear scan, but it stays regardless.
Edge — out of range ( or ) in a nonempty array.
There is no -th order statistic, so robust Select must reject this up front (return an error / raise), before the base case runs. Without the guard, index arithmetic like can silently return garbage or crash.
Edge — duplicates equal to straddling the partition.
They all land in , never split across and . If we return immediately; otherwise we recurse on the strictly-greater side, which is guaranteed smaller.
Edge — (empty array).
Selection is undefined — there is no -th element. A correct implementation guards against it before the base case; the recurrence assumes .
Recall One-line self-test

The single inequality the whole topic rests on? ::: — strictly less than one is what makes collapse to .