Quickselect

Context: FIT2004_MOC · Partitioning (Quicksort) reused for a different problem — find the -th smallest without sorting · the canonical decrease-and-conquer win: recurse into one side, not both · worst case eliminated by Median of Medians

Quick Revision

  • 🎯 Objective: partition once, then discard the side that cannot contain rank ➔ expected, beating sort-then-index’s .
  • 📦 Core Components: partition (shared with Quick Sort) ➔ pivot lands final at index | compare to ➔ decision | one-sided recursion ➔ the entire difference from quicksort.
  • ⚡ Key Constraint: is expected, not guaranteed — bad pivots give ; only a Median of Medians pivot makes a worst-case bound.

📝 How It Works

1. -th Order Statistics — the Problem

  • Specification ➔ given unsorted orderable items and a rank , return the element that would sit at index if the list were sorted — without producing the sorted list. Equivalently: return the smallest items, internal order irrelevant.
  • Worked reading ➔ on 21, 84, 16, 14, 79, 51, 66, 21, 54, 32: ➔ 14 · ➔ {14, 16} · ➔ {14, 16, 21, 21, 32}.
  • The whole quartile family is one call ➔ at · median at · at ➔ Measures of Spread and Boxplots. The median is why it matters — it is the pivot Quick Sort wants.
  • “Isn’t this just partition?” ➔ yes — a partition already separates smaller from larger around one element; selection is the question of driving that partition to the rank you want ➔ Partitioning (Quicksort).

2. The Baseline — Sort and Slice

  • The obvious algorithm ➔ sort, then slice the first ⟹ , the cost of one comparison ➔ Sorting Problem.
  • The linear sorts do not rescue it ➔ Counting Sort needs the key range capped; Radix Sort needs bounded key width — neither can be assumed for arbitrary input, so the stands.
  • Sorting is over-solving ➔ it answers every rank at once for ; selection answers one rank for . Choosing sorting when a single rank is asked is the LO3 error.

3. One-Sided Recursion — The Whole Idea

  • Partition tells you the pivot’s TRUE rank ➔ after partition(lo, hi) returns boundary , the pivot is at its final sorted index, with zero further work.
  • Three-way decision ➔ ⟹ done · ⟹ the answer is in [lo, j-1], throw the right side away · ⟹ recurse on [j+1, hi] with unchanged (absolute indices).
  • Why the cost collapses ➔ Quick Sort recurses on both halves so every level still costs across levels; quickselect recurses on one, so the level costs halve — a Geometric Series with summing to .

4. Cost Profile

  • Best case is , not ➔ even a first-try hit has already paid for one full partition ⟹ the whole input is read no matter what.
  • Expected ➔ under balanced pivots ⟹ root-dominated () ➔ Solving Recurrences (Telescoping). (The lecturer’s formal average-case derivation — multiply by , subtract the instance so the summations cancel — is flagged NOT EXAMINABLE.)
  • Worst ➔ an extreme pivot every level peels one element ⟹ by the Arithmetic Series — same failure mode and same inputs as Quick Sort.
  • Auxiliary space is when written iteratively ➔ the recursion is tail recursion, so it rewrites as a while loop over lo/hi ⟹ genuinely in-place, unlike Quick Sort, whose first call cannot be eliminated.
  • Destructive ➔ partitioning permutes the caller’s array; if the original order matters, copy first and pay space.

5. Feeding Quick Sort a Pivot — the Exact Median Fails

  • The tempting plan ➔ call quickselect for the exact median, use it as the pivot, and skip quicksort’s own partition — quickselect already partitioned the range.
  • It does not work ➔ quickselect’s own worst case is , so the pivot-finding call at the root costs and the level sum is ⟹ the sort stays . Derivation in Applied Exercise 2.
  • The fix is to stop demanding the exact median ➔ any pivot inside the middle band suffices, because a constant-fraction split already gives logarithmic depth ➔ Quick Sort §3.
  • Median of Medians delivers exactly that band ➔ a pivot provably between the th and th percentile, found in worst case ⟹ height , partition already done ⟹ worst-case quicksort. Lecturer’s coda: “in reality, random pivot works well due to probability” — the guarantee is bought with a constant factor nobody pays voluntarily.

6. Adapting Quickselect to a New Problem (applied W4 — LO1)

  • The reusable skeleton ➔ partition once ➔ read something off the pivot’s position ➔ recurse into the single side that can still contain the answer. Every variant changes only what is read off and which side survives.
  • Weighted median ➔ each carries weight with ; the answer is the whose left and right weight sums are each . Swap “compare rank to ” for “compare weight sums to ”: if neither side exceeds the pivot is the answer, otherwise recurse into the heavier side with reduced by the weight skipped. Same expected.
  • The closest to the median ➔ run quickselect once for the median , then again for the -th smallest ; take everything strictly below that value and top up to from the ties. Two linear calls ⟹ .
  • Why the top-up is mandatory ➔ the elements are unique but their distances are not ( and tie), so “strictly closer than the -th” can yield fewer than ; any tied element will do, being equally close.
  • Killing the space ➔ do not materialise the absolute-difference copy; run a modified quickselect that interprets array[i] as on the fly ⟹ extra space beyond the output.
  • Order statistic across two sorted arrays [D] ➔ is monotone in , hence binary-searchable: an inner Binary Search on inside an outer one on gives ; running the two searches simultaneously removes the nesting for .

⚙️ Core Implementation

🔹 Iterative quickselect — auxiliary

🔹 Weighted quickselect — the same skeleton, a different read-off

⚖️ Core Decision Matrix

ApproachTimeAuxiliary spaceNeeds all upfront?Selection rule
Sort, then slice merge · quickYesmany ranks queried, or the sorted list is wanted anyway
Quickselect (random pivot) best/expected, worst iterativeYesone rank, array in memory, mutation acceptable
Quickselect Median of Medians worstYesa guarantee is required (real-time, adversarial input)
Size- heap ➔ Online AlgorithmNo — streams unknown or unbounded, or all smallest wanted as they arrive

When It Flips: quickselect wins while exactly one rank is needed from a resident array. Ask for different ranks and sorting once amortises better; drop the "resident array" assumption and quickselect is unusable at any cost, because it must partition the whole input — that regime belongs to the size- heap in Online Algorithm.

📊 Exam Execution Trace & Applied Exercises

Manual Execution Trace

quickselect([7, 2, 9, 1, 5, 8, 3], k=2) — the rd smallest. Pivot middle element of the live window (Lomuto):

StepWindow [lo, hi]PivotArray after partitionBoundary Decision
0 (Init)[0, 6]—[7, 2, 9, 1, 5, 8, 3]——
1[0, 6][1, 2, 9, 7, 5, 8, 3] ⟹ lo = 1
2[1, 6][1, 3, 2, 5, 7, 8, 9] ⟹ hi = 3
3[1, 3][1, 2, 3, 5, 7, 8, 9] ⟹ lo = 2
4[2, 3][1, 2, 3, 5, 7, 8, 9] ⟹ return

Window sizes — discarded halves are never revisited, and the array is left partially sorted only: rank is correct and nothing else is guaranteed.

Applied Exercise 1 — where quicksort’s went

Problem: derive quickselect’s expected time from its recurrence and contrast the level sum with Quick Sort’s.

Final Extracted Output: expected — the factor is bought by the second recursive call, so deleting it deletes the factor. Same depth, different total. Worst case is still via .

Applied Exercise 2 — why an exact-median pivot does not fix quicksort

Problem: quicksort picks its pivot by calling quickselect for the exact median. Bound the result in the worst case.

Final Extracted Output: the split is now perfect at every level and the sort is still quadratic — the cost moved from the recursion shape into the pivot-finding step. Only a pivot rule linear in the worst case (Median of Medians) closes it, and a merely pivot suffices because constant-fraction splits already give depth.

Applied Exercise 3 — the probability argument for expected

Problem: call a pivot good if it falls in the middle . Bound quickselect’s expected work.

Final Extracted Output: expected. Two reproducible moves: (i) a constant-fraction shrink turns level costs into a Geometric Series summing to a constant multiple of ; (ii) a pivot good with probability costs attempts in expectation, multiplying the bound by a constant. The identical argument with both halves kept gives Quick Sort’s .

⚠️ Common Mistakes

  • 💡 Quoting with no case ➔ is the best/expected bound; the worst case is unless you name the pivot policy. “Quickselect is linear” is only true of the Median of Medians variant ➔ Big-O Notation.
  • 💡 Claiming a best case better than ➔ hitting on the first try still costs one full partition.
  • 💡 Translating when recursing right ➔ with absolute indices never changes; subtracting is only correct if the recursive call re-indexes from . Pick one convention and state it.
  • 💡 Assuming the array is sorted afterwards ➔ only rank is final; the two sides are partitioned, not ordered.

🧠 Active Recall