// Deterministic pivot in O(n): groups of 5, recurse on medians
function medianOfMedians(arr, lo = 0, hi = arr.length - 1) {
const n = hi - lo + 1;
if (n <= 5) {
const slice = arr.slice(lo, hi + 1).sort((a, b) => a - b);
return slice[Math.floor(slice.length / 2)];
}
const meds = [];
for (let i = lo; i <= hi; i += 5) {
const r = Math.min(i + 4, hi);
const g = arr.slice(i, r + 1).sort((a, b) => a - b);
meds.push(g[Math.floor(g.length / 2)]);
}
return medianOfMedians(meds, 0, meds.length - 1);
}
function selectDeterministic(arr, k, lo = 0, hi = arr.length - 1) {
while (true) {
if (lo === hi) return arr[lo];
const pivot = medianOfMedians(arr, lo, hi);
// partition around pivot value (not index), using 3-way partition
let lt = lo, i = lo, gt = hi;
while (i <= gt) {
if (arr[i] < pivot) { [arr[lt], arr[i]] = [arr[i], arr[lt]]; lt++; i++; }
else if (arr[i] > pivot) { [arr[i], arr[gt]] = [arr[gt], arr[i]]; gt--; }
else { i++; }
}
if (k < lt) hi = lt - 1;
else if (k > gt) lo = gt + 1;
else return arr[k];
}
}
- Guarantee: O(n) worst case by ensuring a constant fraction is discarded each iteration.
- Why groups of 5? Ensures at least 3/10 fraction on each side after partitioning (tight recurrence).
- Practice: constant factors are higher than randomized Quickselect; often combine heuristics.