DSA DSA In Depth
0%
lesson quickselectbfprtorder-statistics

Order Statistics: Randomized Quickselect vs Deterministic BFPRT Median-of-Medians

7 min read Module 2.2: Sorting Mechanics, Hybrid Sorts & Linear-Time Order Statistics Updated Oct 23, 2026
📄 Cheat Sheet
Linear-Time Selection: Deterministic Median-of-Medians (BFPRT) Algorithm
Algorithms by Sharma Thankachan Linear-Time Selection: Deterministic Median-of-Medians (BFPRT) Algorithm Watch on YouTube ↗

Proves why grouping elements into columns of 5 guarantees a 30/70 partition split and solves T(n) <= T(n/5) + T(7n/10) + O(n) = O(n).

0:00 / --:--

Hoare's Quickselect (FIND, 1961): Expected $\Theta(N)$ Selection

Given an unsorted array $A[0 \dots n-1]$ and a target rank $k \in [0, n-1]$, the selection problem asks to rearrange $A$ (like C++'s std::nth_element) such that $A[k]$ holds the element that would appear at index $k$ if $A$ were sorted, with $A[0 \dots k-1] \le A[k] \le A[k+1 \dots n-1]$.

Randomized Quickselect picks a random pivot, partitions $A[lo \dots hi]$ around it to land at index $p$, and branches three ways:

  • If $k == p$: Done! Return $A[p]$.
  • If $k < p$: Recurse (or loop) only on the left subarray $A[lo \dots p-1]$.
  • If $k > p$: Recurse (or loop) only on the right subarray $A[p+1 \dots hi]$.

Because a random pivot lands in the middle $50\%$ of the current subarray (ranks $[n/4, 3n/4]$) with probability $1/2$, every $2$ expected partitions shrink the active subarray to at most $\frac{3}{4}$ of its previous size. Summing the geometric series gives:

$$\mathbb{E}[T(n)] \le \sum_{j=0}^{\infty} 2 \cdot c n \left(\frac{3}{4}\right)^j = 8cn = \Theta(n)$$

More precisely, using indicator random variables $X_{ij}$, Quickselect performs at most $2(n + 1 + (k+1)\ln\frac{n}{k} + (n-k)\ln\frac{n}{n-k}) \le (2 + 2\ln 2)n \approx 3.386n$ expected comparisons to find the median, and $2n + o(n)$ to find the minimum or maximum.

The BFPRT (Blum-Floyd-Pratt-Rivest-Tarjan, 1973) Median-of-Medians Proof

Can we guarantee a good pivot deterministically in $O(n)$ worst-case time? Suppose we partition $n$ elements into $\lceil n/5 \rceil$ groups of $5$, sort each group of $5$ in $O(1)$ comparisons (at most 6 comparisons per group), and recursively call bfprt_select on the $\lceil n/5 \rceil$ group medians to find their exact median $x$ (the Median of Medians).

How many elements are guaranteed to be $\le x$ (and symmetrically $\ge x$)?

  • At least half of the $\lceil n/5 \rceil$ group medians are $\le x$ (that is, $\ge \frac{1}{2}\lceil n/5 \rceil$ groups).
  • In every such group of $5$ (ignoring the one partial group and the pivot's own group), the group median and the $2$ smaller elements in its column of $5$ are all $\le x$—contributing $3$ elements per group!
  • Therefore, at least $3\left(\left\lceil \frac{1}{2}\left\lceil \frac{n}{5} \right\rceil \right\rceil - 2\right) \ge \frac{3n}{10} - 6$ elements are $\le x$, and symmetrically at least $\frac{3n}{10} - 6$ elements are $\ge x$.
  • Consequently, whichever side of $x$ we recurse into after partitioning contains at most $\frac{7n}{10} + 6$ elements!

This yields the celebrated BFPRT recurrence:

$$T(n) \le \underbrace{T\!\left(\left\lceil \frac{n}{5} \right\rceil\right)}_{\text{find median of medians}} + \underbrace{T\!\left(\frac{7n}{10} + 6\right)}_{\text{recurse on survivor side}} + \underbrace{\Theta(n)}_{\text{5-sorts + partition}}$$

Because $\frac{1}{5} + \frac{7}{10} = \frac{9}{10} < 1$, the work decays geometrically at every level of the recursion tree ($1 + 0.9 + 0.81 + \dots = 10$), proving by induction (or Akra-Bazzi with $p = 1$) that $T(n) \le 10cn = \Theta(n)$ in the strict worst case!

Why Groups of $5$? Why Do Groups of $3$ Fail While Groups of $7$ Work?

Quickselect & BFPRT Median-of-Medians 3n/10 Elimination Grid
Expected O(N) / Worst-Case O(N)

Inspect the 5-column BFPRT Median-of-Medians grid: the quadrant of elements ≤ Median-of-Medians M (and ≥ M) contains at least 3n/10 elements, guaranteeing T(n) ≤ T(n/5) + T(7n/10) + O(n) = O(n).

Step 1 / 7
Custom Input:
Timeline

Invariant & State

BFPRT Recurrence: At least ⌈n/10⌉ column medians are ≤ M, and each contributes 3 elements ≤ M ⇒ ≥ 3n/10 elements eliminated! Since 1/5 + 7/10 = 9/10 < 1, T(n) = O(n).
📊 Live Magnitude & Pointer Stage — Quickselect & BFPRT Median-of-Medians 3n/10 Elimination Grid Active Pivot Match/Sorted
3 of 5 Col 1 … [0] 3 of 5 Col 2 … [1] Median- Pivot … [2] 3 of 5 Col 4 … [3] 3 of 5 Col 5 … [4]
[0]Col 1 Med: 14 (≤M)3 of 5 ≤ 29
[1]Col 2 Med: 22 (≤M)3 of 5 ≤ 29
[2]Pivot M = 29Median-of-Medians
[3]Col 4 Med: 41 (≥M)3 of 5 ≥ 29
[4]Col 5 Med: 53 (≥M)3 of 5 ≥ 29
Guaranteed Eliminated
≥ 3n / 10 elements
Worst-Case Subproblem
≤ 7n / 10 elements
Work Sum Ratio
1/5 + 7/10 = 9/10 < 1 ⇒ Θ(n)
[Step 1] Selected Median-of-Medians M=29; guaranteed at least 30% of elements lie on each side of M.
Group Size $g$ Median-of-Medians Call Max Survivor Partition Subproblem Fraction Sum Worst-Case Complexity
$g = 3$ $T(n / 3)$ $T\!\left((1 - \frac{2}{6})n\right) = T(2n / 3)$ $\frac{1}{3} + \frac{2}{3} = 1.00$ (No decay!) $\Theta(n \log n)$ — Fails!
$g = 5$ (BFPRT Canonical) $T(n / 5)$ $T\!\left((1 - \frac{3}{10})n\right) = T(7n / 10)$ $\frac{1}{5} + \frac{7}{10} = 0.90 < 1$ $\Theta(n)$ — Smallest odd $g$ that works!
$g = 7$ $T(n / 7)$ $T\!\left((1 - \frac{4}{14})n\right) = T(5n / 7)$ $\frac{1}{7} + \frac{5}{7} = \frac{6}{7} \approx 0.857 < 1$ $\Theta(n)$
Introselect (std::nth_element) 0 (Fast 3-sample / Ninther) Switches to BFPRT / Heap on bad splits Adaptive hybrid $\Theta(n)$ worst-case + $\approx 3.4n$ average
BFPRT Grid of 5-Element Sorted Columns: Shaded Upper-Left Quadrant (≥ 3n/10) is Guaranteed ≤ x m₁ ≤ x m₂ ≤ x m₃ ≤ x x (MoM) m₅ ≥ x m₆ ≥ x m₇ ≥ x Green cells (top 3 rows × left half of columns ≥ 3n/10) are ≤ x; Red cells (bottom 3 rows × right half ≥ 3n/10) are ≥ x.
flowchart TD A["Select k-th Smallest in A[lo..hi]"] --> B["Fast Path: Randomized / Median-of-3 Quickselect"] B --> C{"Did Partition Shrink Subarray by ≥ 1/8?"} C -- "Yes (Common Case)" --> D["Continue Iterative Quickselect Loop O(N) Expected (~3.4N cmps)"] C -- "No (Adversarial Split Detected)" --> E["Fallback to Deterministic BFPRT Median-of-Medians (5-Columns) Strict Worst-Case O(N) Guarantee!"]
Hardware Reality: Why std::nth_element Uses Introselect Instead of Pure BFPRT

Although BFPRT runs in strict worst-case $O(n)$ time, its constant factor is $\approx 16n\text{–}24n$ comparisons and requires recursive median-of-medians passes that touch memory multiple times—making pure BFPRT slower than sorting the entire array with std::sort up to $n \approx 10^6$! Consequently, C++ standard libraries (libstdc++ and libc++) implement David Musser's Introselect (or Alexandrescu's Adaptive Quickselect / Median of Ninthers): it runs standard Median-of-Three Hoare Quickselect in a tight iterative loop ($approx 3n$ comparisons) and only invokes BFPRT (or Heapselect) if the recursion depth/bad-split counter exceeds a logarithmic threshold.

Production C++20 Implementation: Iterative 3-Way Quickselect & In-Place BFPRT

#include <algorithm>
#include <concepts>
#include <cstddef>
#include <utility>

template <std::totally_ordered T>
void small_insertion_sort(T* a, std::size_t lo, std::size_t hi) noexcept {
    for (std::size_t i = lo + 1; i <= hi; ++i) {
        T v = std::move(a[i]);
        std::size_t j = i;
        while (j > lo && v < a[j - 1]) {
            a[j] = std::move(a[j - 1]);
            --j;
        }
        a[j] = std::move(v);
    }
}

// Deterministic In-Place BFPRT (Median-of-Medians) with 3-Way Partitioning for Duplicates
template <std::totally_ordered T>
void bfprt_select(T* a, std::size_t lo, std::size_t hi, std::size_t k) noexcept {
    while (hi - lo > 16) {
        const std::size_t n = hi - lo + 1;
        const std::size_t num_groups = n / 5;

        // 1. Sort each group of 5 in-place and swap its median to the front [lo .. lo + num_groups - 1]
        for (std::size_t g = 0; g < num_groups; ++g) {
            const std::size_t g_lo = lo + g * 5;
            small_insertion_sort(a, g_lo, g_lo + 4);
            std::swap(a[lo + g], a[g_lo + 2]);
        }

        // 2. Recursively select the exact median of the group medians at a[lo + num_groups / 2]
        const std::size_t mom_idx = lo + num_groups / 2;
        bfprt_select(a, lo, lo + num_groups - 1, mom_idx);
        const T pivot = a[mom_idx];

        // 3. 3-Way Dutch National Flag Partition around pivot (handles all-equal keys in O(N)!)
        std::size_t lt = lo, i = lo, gt = hi;
        while (i <= gt) {
            if (a[i] < pivot)      std::swap(a[lt++], a[i++]);
            else if (pivot < a[i]) std::swap(a[i], a[gt--]);
            else                   ++i;
        }

        // 4. Tail-loop into whichever region contains target rank k
        if (k < lt) {
            hi = lt - 1;
        } else if (k > gt) {
            lo = gt + 1;
        } else {
            return; // k lies inside [lt .. gt], so a[k] == pivot is in its exact final spot!
        }
    }
    small_insertion_sort(a, lo, hi);
}
Why BFPRT Must Use 3-Way Partitioning on Duplicate Keys

In the classical textbook proof of BFPRT, all elements are assumed to be distinct. If an input array contains $n$ identical elements and you use a 2-way Lomuto partition inside BFPRT, the median-of-medians pivot $x$ is equal to every element, yet 2-way Lomuto places all $n - 1$ equal elements on one side—breaking the $\le 7n/10$ size bound and degrading BFPRT to $\Theta(n^2)$! Using 3-way Dutch National Flag partitioning inside BFPRT and Quickselect guarantees that all elements strictly $< x$ ($ge 3n/10$ minus duplicates) and strictly $> x$ are separated while all duplicates equal to $x$ are coalesced into $[lt \dots gt]$ in one linear pass.

Check yourself Question 1 of 4
Concept Check Why Quickselect Runs in O(N) Expected Time While Quicksort Takes O(N log N) recall

Both Randomized Quicksort and Randomized Quickselect execute the exact same linear-time partition step at the root ($\Theta(n)$ work).

⚡ Interactive Challenge · Staff Level

Algorithmic Design & Complexity Check

Analyze the invariant, asymptotic bound, and hardware behavior before selecting.

Which standard-library pipeline achieves the optimal asymptotic and wall-clock complexity $\Theta(n + k \log k)$?

Key Takeaways

Randomized Quickselect recurses on only one side of the partition, shrinking the subarray geometrically to run in Θ(n) expected time (~3.39n comparisons for the median).
BFPRT (Median-of-Medians) partitions elements into groups of 5, sorts each group, and recursively finds the median of those n/5 medians as its pivot.
At least half of the n/5 group medians are <= the Median-of-Medians x, and each contributes 3 elements <= x, guaranteeing at least 3n/10 elements are eliminated on either side.
The recurrence T(n) <= T(n/5) + T(7n/10) + Θ(n) solves to worst-case Θ(n) because 1/5 + 7/10 = 9/10 < 1 (whereas groups of 3 give 1/3 + 2/3 = 1 and degrade to Θ(n log n)).
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue