This deck covers quicksort's partition step and its recursion, the best, average, and worst-case running times, and the sorted input that triggers the worst case. It then explains randomized pivots and why they make the bad case vanishingly unlikely, introduces the selection problem and quickselect, and gives the median-of-medians pivot rule, using groups of five, that guarantees linear worst-case selection. It targets the myths that quicksort is always n log n, that selection requires a full sort, and that the choice of pivot does not matter for the worst case, and it explains why groups of five in particular are used.
Subject: CS3000 Algorithms · 135 slides · symbolic lesson
Open the interactive version of this deck · Homework for this lesson
Objectives
Quicksort and its cousins are the workhorses behind most real sorting and ranking code. By the end of this lesson you can:
1. Explain the partition step and how quicksort recurses on the two resulting pieces.
2. State quicksort's best, average, and worst-case running times, and name the input that triggers the worst case.
3. Explain why choosing the pivot at random makes the bad case vanishingly unlikely.
4. Solve the selection problem, finding the k-th smallest item, with quickselect, without sorting the whole list.
5. Describe the median-of-medians pivot rule and explain why it guarantees fast selection even in the worst case.
Warm-up
Discussion prompt
Before we open Quicksort, Selection & Median of Medians: without looking back, what was the main idea of Merge Sort & the Master Theorem, and what could you do by the end of it that you could not do before?
Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.
Answer:
That deck traces merge sort end to end, explains why the merge step is linear, sets up the merge sort recurrence, and works the Master Theorem's three cases by comparing the extra work f(n) against the watershed function n^log_b(a). It targets swapping a and b, mis-comparing f(n) to the watershed, assuming the merge step is free, and force-fitting recurrences - those with log-factor gaps or unequal splits - that do not satisfy the theorem's hypotheses.
Concept
Before any new material: cover the screen.
You have named 11 reusable moves so far. Say as many as you can out loud, by number, from memory.
Do not advance until you have actually tried. Getting four of eight is information; skipping the exercise is not.
Here they are. Score yourself.
Today adds no new moves. Every proof in this lesson is built out of the list above. That is the whole point of the list.
The question that starts every proof from here on is not how do I begin. It is which of these applies here?
Counterexample
Discussion prompt
You have named 11 reusable moves so far. Say as many as you can out loud, by number, from memory.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Answer:
Do not advance until you have actually tried. Getting four of eight is information; skipping the exercise is not.
Concept
Quicksort sorts a list by picking one item to act as a pivot, rearranging the list so everything smaller comes before it and everything bigger comes after it, then sorting each of those two pieces the same way.
That rearranging step is called partitioning. Once a list is partitioned around a pivot, the pivot itself never needs to move again, only the two pieces on either side still need sorting.
Picture it
Animation
Shows: Partition around a pivot — a rendered Manim animation.
Rendered with Manim.
Takeaway: One pass, and the pivot never moves again.
Intuition
Picture a messy pile of exam papers. You grab one paper at random, call it the pivot, and sort every other paper into a smaller pile or a bigger pile compared to it.
The pivot paper is now exactly where it belongs, nothing smaller is after it, nothing bigger is before it. You repeat the same trick on each of the two smaller piles, and keep going until every pile has just one paper left.
Analogy
Discussion prompt
Explain Splitting one pile into two by analogy to something with no CS3000 Algorithms in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
Picture a messy pile of exam papers. You grab one paper at random, call it the pivot, and sort every other paper into a smaller pile or a bigger pile compared to it.
Concept
pivot — The one item chosen to compare every other item against during a partition step. After partitioning, the pivot sits in its final sorted position.
partition — Rearranging a list around a chosen pivot so every item before the pivot is less than or equal to it, and every item after the pivot is greater than or equal to it.
Partitioning does not sort either side, it only guarantees the split is in the right order relative to the pivot. Each side still needs its own sorting pass.
Explain it
Discussion prompt
Explain What it means for a list to be "partitioned" to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
Partitioning does not sort either side, it only guarantees the split is in the right order relative to the pivot. Each side still needs its own sorting pass.
Concept
Pick the pivot (a common choice: the last item in the range). Then walk through every other item once, left to right.
Keep a boundary marking how many items so far belong in the smaller-or-equal group. Whenever the current item is smaller than or equal to the pivot, swap it just past that boundary and move the boundary forward.
After the single pass, swap the pivot into the spot right after the boundary. Everything before it is smaller-or-equal, everything after it is bigger. One pass through the list is all a single partition costs.
Concept
Partition is the engine of both quicksort and quickselect, and it is one loop. A boundary marches through the array, and everything behind it is known to be small.
PARTITION(A, lo, hi)
pivot = A[hi]
b = lo - 1
for j = lo to hi - 1
if A[j] <= pivot
b = b + 1
swap A[b] with A[j]
swap A[b + 1] with A[hi]
return b + 1The variable b is a boundary, not a counter. Everything from lo to b is at most the pivot, and everything from b plus one to j minus one is greater. Line 8 finally drops the pivot into the gap between them.
Notation
Every line of PARTITION says one thing. Read the line, then read what it does — not the other way round.
Annotate
Invariant
Everything left of the boundary is at most the pivot, and everything between the boundary and the scanner is greater. Both statements stay true after every single pass of the loop.
Step through it
At each step, say which region the scanner's current item is about to join.
Picture it
Animation
Shows: PARTITION executing: the current line of pseudocode is highlighted while the data it touches changes.
Rendered with Manim.
Takeaway: One left-to-right pass and a moving boundary put the pivot in its final place — linear time, and no extra array.
Concept
Quicksort's partition step only swaps items within the original list. It never copies the list into new, separate arrays the way some other sorting methods do.
That is one reason quicksort is popular in practice: it sorts using very little extra memory beyond the list itself, even though it uses more memory behind the scenes for the recursive calls.
Picture it
Figure (svg): Seven boxes in a row holding the numbers 8, 3, 7, 4, 9, 2, 5, with the last box labeled as the pivot
Discussion prompt
Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.
Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.
Answer:
Partition the list 8, 3, 7, 4, 9, 2, 5 using the last item, 5, as the pivot.
Worked example
Partition the list 8, 3, 7, 4, 9, 2, 5 using the last item, 5, as the pivot.
Figure (svg): Seven boxes in a row holding the numbers 8, 3, 7, 4, 9, 2, 5, with the last box labeled as the pivot
Scan left to right, comparing each item to the pivot (5)
Why: 8 is greater than 5, so it stays put for now, nothing to swap yet.
3 is less than or equal to 5, so swap it into the boundary spot
Why: 3 belongs in the smaller-or-equal group. Swapping 8 and 3 gives 3, 8, 7, 4, 9, 2, 5 and moves the boundary forward by one.
7 is greater than 5, so it stays; 4 is less than or equal to 5, so swap it in
Why: Swapping 8 and 4 gives 3, 4, 7, 8, 9, 2, 5, moving the boundary forward again.
9 is greater than 5, so it stays; 2 is less than or equal to 5, so swap it in
Why: Swapping 7 and 2 gives 3, 4, 2, 8, 9, 7, 5, moving the boundary forward one more time.
Swap the pivot into the spot right after the boundary
Why: Swapping 5 with 8 gives the final partitioned list: 3, 4, 2, 5, 9, 7, 8. The pivot 5 now sits exactly where it belongs.
| item scanned | compared to pivot 5 | list after this step |
|---|---|---|
| 8 | greater, skip | 8, 3, 7, 4, 9, 2, 5 |
| 3 | less or equal, swap in | 3, 8, 7, 4, 9, 2, 5 |
| 7 | greater, skip | 3, 8, 7, 4, 9, 2, 5 |
| 4 | less or equal, swap in | 3, 4, 7, 8, 9, 2, 5 |
| 9 | greater, skip | 3, 4, 7, 8, 9, 2, 5 |
| 2 | less or equal, swap in | 3, 4, 2, 8, 9, 7, 5 |
| pivot 5 | swap into final spot | 3, 4, 2, 5, 9, 7, 8 |
Verify the partition is correct
Why: Everything before the pivot (3, 4, 2) is less than or equal to 5, and everything after it (9, 7, 8) is greater than or equal to 5. The pivot sits at its final sorted position.
Comparison
Comparison matrix
From Worked example: partition one array by hand: refill the compared to pivot 5 column from what you know. The rest of the table is as it appeared.
| item scanned | compared to pivot 5 | list after this step |
|---|---|---|
| 8 | greater, skip | 8, 3, 7, 4, 9, 2, 5 |
| 3 | less or equal, swap in | 3, 8, 7, 4, 9, 2, 5 |
| 7 | greater, skip | 3, 8, 7, 4, 9, 2, 5 |
| 4 | less or equal, swap in | 3, 4, 7, 8, 9, 2, 5 |
| 9 | greater, skip | 3, 4, 7, 8, 9, 2, 5 |
| 2 | less or equal, swap in | 3, 4, 2, 8, 9, 7, 5 |
| pivot 5 | swap into final spot | 3, 4, 2, 5, 9, 7, 8 |
Concept
Once a partition places the pivot correctly, quicksort calls itself on the piece before the pivot and, separately, on the piece after it.
The pivot itself is left alone from here on, it already sits in its final position. Each recursive call partitions its own smaller piece the same way, choosing a new pivot from within that piece.
Intuition
Every partition places one item, the pivot, into its final spot and hands off two strictly smaller pieces to sort. The pieces can never be as big as the piece that produced them.
Eventually a piece has zero or one items left. A piece that small is already sorted by definition, so the recursion simply stops there, no further pivoting is needed.
Ranking
Put in order
Put the moves of Worked example: quicksort start to finish into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. Around pivot 5, the list becomes the piece 3, 4, 2, then 5, then the piece 9, 7, 8.
Worked example
Continue sorting 8, 3, 7, 4, 9, 2, 5 using the last item of each piece as its pivot.
First partition (shown earlier) splits the whole list
Why: Around pivot 5, the list becomes the piece 3, 4, 2, then 5, then the piece 9, 7, 8.
Partition the left piece, 3, 4, 2, around pivot 2
Why: 2 is smaller than both 3 and 4, so it ends up first: the piece becomes 2, then the remaining piece 4, 3.
Partition 4, 3 around pivot 3
Why: 3 is smaller than 4, so they swap to 3, 4, pivot 3 is first, leaving the single-item piece 4.
| call | piece being sorted | pivot used | result |
|---|---|---|---|
| 1 | 8, 3, 7, 4, 9, 2, 5 | 5 | (3, 4, 2), 5, (9, 7, 8) |
| 2 | 3, 4, 2 | 2 | (), 2, (4, 3) |
| 3 | 4, 3 | 3 | (), 3, (4) |
| 4 | 9, 7, 8 | 8 | (7), 8, (9) |
Partition the right piece, 9, 7, 8, around pivot 8
Why: 7 is smaller than 8 and swaps forward; 9 stays put. The piece becomes 7, then 8, then the single-item piece 9.
Every piece is now down to zero or one items, so stitch the results back together in order
Why: Reading left to right: 2, then 3, then 4, then the original pivot 5, then 7, then 8, then 9.
\[ 2,\ 3,\ 4,\ 5,\ 7,\ 8,\ 9 \]
Verify the final order is fully sorted
Why: The original items were 8, 3, 7, 4, 9, 2, 5, the same seven values, now listed from smallest to largest with none missing or repeated.
Picture it
Animation
Shows: Randomisation removes the bad input — a rendered Manim animation.
Rendered with Manim.
Takeaway: No adversary can pick an input that is reliably bad.
Concept
Every partition costs one pass through the current piece, proportional to that piece's size. What changes from case to case is how many more partitions are still needed afterward.
If a pivot splits its piece into two roughly equal halves, the two recursive calls attack much smaller pieces. If a pivot splits its piece into one tiny piece and one almost-as-big piece, barely any progress was made.
Concept
Every proof of this kind has the same five or six moves in the same order. The order is not something you rediscover each time.
It is on the right. It will stay on the right through the worked examples that follow.
Why this matters: the structure is now handled. You are not spending working memory on what comes next — you are spending all of it on the one hard step.
Step 4 is the one people skip. A running time that is only true for friendly inputs is not a running time — worst case means an adversary picks the input after seeing your code.
Intuition
Draw one box for the original list, then two boxes underneath for the two pieces its first partition produces, then boxes underneath those for their pieces, and so on.
If every split is close to even, the boxes shrink toward size one quickly, the tree is short and wide. If a split keeps peeling off just one item at a time, the tree is tall and thin, with barely any shrinking per level.
Concept
If every pivot happens to land exactly in the middle of its piece, each level of the recursion tree does a total amount of work proportional to the size of the whole original list.
\[ T(n) = 2\,T\!\left(\frac{n}{2}\right) + \Theta(n) \]
A list of size n can only be cut in half so many times before reaching size one, about that many levels exist. Multiplying the per-level cost by the number of levels gives the best-case running time.
\[ T(n) = \Theta(n \log n) \]
Step zero
Discussion prompt
Worked example: checking the n log n shape for n = 8 — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Count how many times 8 can be halved before reaching size 1
Answer:
Worked example
Suppose a list of 8 items always splits into two even halves.
Count how many times 8 can be halved before reaching size 1
Why: 8, 4, 2, 1, that is 3 halvings, so the recursion tree has 3 levels below the top, or about log base 2 of 8 levels.
\[ \log_{2}(8) = 3 \]
Add up the partitioning cost at each level
Why: The top level partitions 8 items, the next level partitions two pieces of 4 (still 8 items total), the next partitions four pieces of 2 (still 8 items total). Each level costs about 8.
| level | pieces | total items partitioned |
|---|---|---|
| 0 | 1 piece of size 8 | 8 |
| 1 | 2 pieces of size 4 | 8 |
| 2 | 4 pieces of size 2 | 8 |
Verify the total matches the n log n shape
Why: Three levels at a cost of about 8 each gives roughly 8 times 3, which is 24, the same shape as n times log base 2 of n for n equal to 8, since log base 2 of 8 is 3.
\[ 8 \times \log_{2}(8) = 8 \times 3 = 24 \]
Picture it
Animation
Shows: Each line of the worked example "checking the n log n shape for n = 8", appearing one at a time.
The same working the example does, in the order a tutor would write it.
Takeaway: Three levels at a cost of about 8 each gives roughly 8 times 3, which is 24, the same shape as n times log base 2 of n for n equal to 8, since log base 2 of 8 is 3.
Concept
If every pivot happens to be the smallest or the largest item in its piece, one side of the partition is empty and the other side has everything else.
\[ T(n) = T(n-1) + \Theta(n) \]
Now barely any progress is made per partition, the piece only shrinks by one item at a time. Adding up the shrinking costs gives a running time that grows with the square of the list's size.
\[ T(n) = \Theta(n^{2}) \]
Intuition
The worst case is not about bad luck in general, it happens whenever the pivot rule keeps picking an extreme item on purpose.
The classic trigger: a list that is already sorted (or reverse sorted), combined with a pivot rule that always picks the first (or last) item. On sorted input, the first item is always the smallest of whatever is left, so every partition peels off exactly one item.
Intuition
What move should we make next?
The claim on the table:
\[ \text{quicksort runs in } \Theta(n \log n) \]
You are now the adversary. You get to choose the array after seeing that the pivot is always the first element.
This is a for-all claim about every input. Which move do you reach for, and what would you build?
_Look at your toolkit. Say a move number out loud before this slide advances._ A wrong guess is useful. A silent guess is not.
Picture it
Animation
Shows: Two partition schemes — a rendered Manim animation.
Rendered with Manim.
Takeaway: Same asymptotics, noticeably different constants.
Estimation
Predict first
Sort the already-sorted list 1, 2, 3, 4, 5 using a pivot rule that always picks the first item of the current piece.
Commit before you compute: what does Worked example: naive pivot on already-sorted input come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: Verify this matches the quadratic worst-case formula
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. The general worst-case comparison count is n times (n minus 1), divided by 2.
Worked example
Sort the already-sorted list 1, 2, 3, 4, 5 using a pivot rule that always picks the first item of the current piece.
Partition 1, 2, 3, 4, 5 around pivot 1
Why: 1 is the smallest item, so every one of the other four items lands on its greater side. The piece becomes 1, then the piece 2, 3, 4, 5, no items removed except the pivot.
| piece size | pivot | comparisons made | remaining piece size |
|---|---|---|---|
| 5 | 1 | 4 | 4 |
| 4 | 2 | 3 | 3 |
| 3 | 3 | 2 | 2 |
| 2 | 4 | 1 | 1 |
| 1 | 5 | 0 | 0 |
Repeat on each remaining piece: 2,3,4,5 then 3,4,5 then 4,5 then 5
Why: Each partition again picks the smallest item as the first element, again peeling off exactly one item and comparing against every item still left.
Add the comparisons made at every step
Why: 4 plus 3 plus 2 plus 1 plus 0 is 10 total comparisons for these 5 items.
\[ 4+3+2+1+0 = 10 \]
Verify this matches the quadratic worst-case formula
Why: The general worst-case comparison count is n times (n minus 1), divided by 2. For n equal to 5, that is 5 times 4 divided by 2, which is also 10, matching the direct count.
\[ \frac{n(n-1)}{2} = \frac{5 \times 4}{2} = 10 \]
Picture it
Animation
Shows: Each line of the worked example "naive pivot on already-sorted input", appearing one at a time.
The same working the example does, in the order a tutor would write it.
Takeaway: The general worst-case comparison count is n times (n minus 1), divided by 2. For n equal to 5, that is 5 times 4 divided by 2, which is also 10, matching the direct count.
Missing information
Discussion prompt
Sort the same list, 1, 2, 3, 4, 5, but this time choose the middle item, 3, as the pivot instead of the first item.
What do you need to know — or decide — before the first line can be written? List everything the problem has to hand you.
Hint: Anything you would have to invent to get started is a thing the problem must supply.
Answer:
Items 1 and 2 are less than 3; items 4 and 5 are greater than 3. The piece splits into 1, 2 on one side and 4, 5 on the other, both size 2.
Worked example
Sort the same list, 1, 2, 3, 4, 5, but this time choose the middle item, 3, as the pivot instead of the first item.
Partition 1, 2, 3, 4, 5 around pivot 3
Why: Items 1 and 2 are less than 3; items 4 and 5 are greater than 3. The piece splits into 1, 2 on one side and 4, 5 on the other, both size 2.
| side | items | size |
|---|---|---|
| smaller than pivot | 1, 2 | 2 |
| pivot | 3 | 1 |
| greater than pivot | 4, 5 | 2 |
Compare this split to the naive pivot's split
Why: The naive first-element pivot produced pieces of size 0 and 4 from this same list. Choosing the middle item instead produced pieces of size 2 and 2, a much more even split from the exact same starting list.
Verify the same input can behave very differently depending on the pivot
Why: Nothing about the list 1, 2, 3, 4, 5 changed between the two examples, only the rule for picking the pivot changed, and that alone flipped the split from maximally unbalanced to perfectly balanced.
Picture it
Animation
Shows: Each line of the worked example "a better pivot on the same sorted list", appearing one at a time.
The same working the example does, in the order a tutor would write it.
Takeaway: Nothing about the list 1, 2, 3, 4, 5 changed between the two examples, only the rule for picking the pivot changed, and that alone flipped the split from maximally unbalanced to perfectly balanced.
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student remembers that quicksort is one of the fast sorting algorithms and assumes its running time is always proportional to n log n, no matter the input or the pivot rule.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: This is exactly the setup from the worked example a few slides back: every partition only removes one item, and the student's assumed running time is simply wrong for this case.
n log n describes the best case and, with a good pivot rule, the average case, it is not a guarantee for every combination of input and pivot rule.
Why: This is exactly the setup from the worked example a few slides back: every partition only removes one item, and the student's assumed running time is simply wrong for this case.
Trap
A student remembers that quicksort is one of the fast sorting algorithms and assumes its running time is always proportional to n log n, no matter the input or the pivot rule.
\[ \text{Claimed always: } T(n) = \Theta(n \log n) \]
Apply that assumption to a sorted list with a first-element pivot
Why: This is exactly the setup from the worked example a few slides back: every partition only removes one item, and the student's assumed running time is simply wrong for this case.
\[ \text{Actual: } T(n) = \Theta(n^{2})\ \text{on this input} \]
n log n describes the best case and, with a good pivot rule, the average case, it is not a guarantee for every combination of input and pivot rule.
\[ \text{Best/average case: } \Theta(n \log n) \]
Name the pivot rule and the input before naming the running time
Why: A sorted list with a first-or-last-element pivot rule genuinely does run in quadratic time. Whether n log n applies depends on that combination, not on quicksort's name alone.
\[ \text{Worst case: } \Theta(n^{2}),\ \text{triggered by sorted input} + \text{naive pivot} \]
Notation
Annotate
From Trap: "quicksort is always n log n" — read this one piece at a time. What is each part doing?
On: \( \text{Claimed always: } T(n) = \Theta(n \log n) \)
Intuition
What feels wrong about this?
Both of these are true about the same algorithm:
\[ \text{worst case: } \Theta(n^{2}), \qquad \text{average case: } \Theta(n \log n) \]
_Plain English only. No notation, no algebra. Just say what bothers you._
The feeling: if the worst case is quadratic, it feels like the average should be dragged somewhere in between — not sitting right down at the good end.
That feeling is the proof. It is not a substitute for the proof — it is the thing the proof writes down.
The resolution is counting: quadratic behaviour needs a badly unbalanced split at nearly every level, and the overwhelming majority of pivot choices are not that bad. Rare disasters barely move an average.
Concept
For a random pivot on a typical (not adversarially chosen) list, the split is usually somewhere in the middle range, not perfectly even, but not maximally lopsided either.
Averaging over all the possible pivot positions, the expected running time works out to the same n log n shape as the best case, just with a somewhat larger constant factor.
\[ \mathbb{E}[T(n)] = \Theta(n \log n) \]
Picture it
Animation
Shows: Why the average is n log n — a rendered Manim animation.
Rendered with Manim.
Takeaway: The split need not be even — only not catastrophic.
Intuition
The worst case needs the pivot to land on an extreme item, the very smallest or very largest, over and over, every single time. Most positions in a piece are not extreme.
Even a so-so split, say one third and two thirds, still removes a real chunk of the piece each time. Only a long, unlucky streak of extreme splits reproduces the quadratic behavior, and that streak is what a fixed, sorted-input-exploiting pivot rule guarantees, not what typical data produces.
Concept
Instead of always picking the first or last item, pick the pivot uniformly at random from the current piece, using a fresh random choice at every single partition call.
This is called randomized quicksort. The algorithm's steps are otherwise identical, only where the pivot comes from has changed.
Picture it
Animation
Shows: The pivot decides everything — a rendered Manim animation.
Rendered with Manim.
Takeaway: Already-sorted input with a first-element pivot hits the worst case.
Intuition
With a fixed pivot rule, like always picking the first item, some specific input (a sorted list) can be built ahead of time to trigger the worst case every single partition.
With a random pivot, no fixed input can do that, because the pivot itself is not determined by the input at all, it depends on a coin flip made fresh each call. A sorted list is no more dangerous than any other list once the pivot is chosen randomly.
Step zero
Discussion prompt
Worked example: what a random pivot buys you — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Find the chance a uniformly random pivot is good
Answer:
Worked example
Call a pivot "good" if it lands somewhere in the middle half of the current piece, not among the smallest quarter or the largest quarter.
Find the chance a uniformly random pivot is good
Why: The middle half of the piece is, by definition, half of all the positions a random pivot could land on.
\[ P(\text{good pivot}) = \frac{1}{2} \]
Work out what a good pivot guarantees
Why: A pivot in the middle half always leaves the larger side with at most three quarters of the piece, never the near-total pieces a worst-case pivot leaves behind.
\[ \text{good pivot} \Rightarrow \text{larger side} \le \frac{3n}{4} \]
Verify that repeated good pivots keep the recursion short
Why: Shrinking a piece by a factor of three quarters, over and over, still only takes about a logarithmic number of shrinks to reach size one, and a random pivot lands "good" about every other try, on average, so the expected depth stays logarithmic even with some unlucky draws mixed in.
Picture it
Animation
Shows: Each line of the worked example "what a random pivot buys you", appearing one at a time.
The same working the example does, in the order a tutor would write it.
Takeaway: Shrinking a piece by a factor of three quarters, over and over, still only takes about a logarithmic number of shrinks to reach size one, and a random pivot lands "good" about every other try, on average, so the expected depth stays logarithmic even with some unlucky draws mixed in.
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student argues that since the worst case is technically still possible for a randomized pivot, some unlucky run of coin flips could always pick an extreme item every time, randomization doesn't actually change anything.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: This mixes up "possible" with "likely." The worst case being reachable in principle says nothing about how often it actually happens, and that gap is exactly the point of randomizing.
Pivot choice controls whether the worst case is something an adversary can force, or something so improbable it essentially never shows up.
Why: This mixes up "possible" with "likely." The worst case being reachable in principle says nothing about how often it actually happens, and that gap is exactly the point of randomizing.
Trap
A student argues that since the worst case is technically still possible for a randomized pivot, some unlucky run of coin flips could always pick an extreme item every time, randomization doesn't actually change anything.
\[ \text{Worst case still exists: } \Theta(n^{2})\ \text{(technically possible)} \]
Conclude that pivot choice is irrelevant to performance
Why: This mixes up "possible" with "likely." The worst case being reachable in principle says nothing about how often it actually happens, and that gap is exactly the point of randomizing.
Pivot choice controls whether the worst case is something an adversary can force, or something so improbable it essentially never shows up.
\[ \text{Fixed pivot on chosen input: worst case is guaranteed} \]
Compare the two pivot rules on the exact same input
Why: A fixed first-element pivot on a sorted list hits the quadratic worst case every single time. A random pivot on that same sorted list has an expected running time of n log n, because good splits happen often enough regardless of how the input was arranged.
\[ \text{Random pivot on any fixed input: } \mathbb{E}[T(n)] = \Theta(n \log n) \]
Translation
\( \text{Random pivot on any fixed input: } \mathbb{E}[T(n)] = \Theta(n \log n) \)
Draw it
Translate both ways. First write the expression above as a sentence with no symbols in it at all. Then cover it, and write your sentence back as notation. If the two versions disagree, the disagreement is the thing to fix.
Pattern
1. Ask how balanced the split is
Why: The running time is entirely a story about split balance, perfectly even splits, wildly uneven splits, and everything in between.
2. Perfectly balanced splits give n log n; maximally unbalanced splits give quadratic
Why: These are the two extremes: even splitting shrinks the piece geometrically every level; one-at-a-time splitting barely shrinks it at all.
3. Name the pivot rule and the input together before claiming a running time
Why: Quicksort alone does not determine performance, a fixed pivot rule paired with an input built to exploit it produces the worst case; a random pivot paired with any input produces expected n log n.
4. Randomizing the pivot trades a guaranteed bad case for an astronomically unlikely one
Why: No input can force randomized quicksort into its worst case on purpose, because the pivot no longer depends on the input at all.
Real world
Discussion prompt
Outside this lesson: where does Quicksort, Selection & Median of Medians actually turn up? Name one concrete situation — a job, a piece of software someone ships, a decision somebody has to make — and say which part of How to reason about quicksort's running time is doing the work in it.
Hint: Vague is the failure mode here. "Engineering" is not a situation; "deciding whether this build is fast enough to ship" is.
Answer:
That deck covers quicksort's partition step and its recursion, the best, average, and worst-case running times, and the sorted input that triggers the worst case. It then explains randomized pivots and why they make the bad case vanishingly unlikely, introduces the selection problem and quickselect, and gives the median-of-medians pivot rule, using groups of five, that guarantees linear worst-case selection. It targets the myths that quicksort is always n log n, that selection requires a full sort, and that the choice of pivot does not matter for the worst case, and it explains why groups of five in particular are used.
Picture it
Animation
Shows: Many duplicates break naive partition — a rendered Manim animation.
Rendered with Manim.
Takeaway: Three-way partitioning fixes this by grouping equals in the middle.
Elimination
Eliminate the wrong options
What is the running time of this sort, and why?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: On sorted input, the first item of any piece is always its smallest item. Picking it as the pivot means every partition peels off exactly one item, leaving a piece one smaller than before, giving the classic quadratic worst-case pattern of n, n minus 1, n minus 2, and so on.
Check
A list of 1,000 already-sorted numbers is quicksorted using a pivot rule that always picks the first item of the current piece.
Check your understanding
What is the running time of this sort, and why?
Answer: A
Why: On sorted input, the first item of any piece is always its smallest item. Picking it as the pivot means every partition peels off exactly one item, leaving a piece one smaller than before, giving the classic quadratic worst-case pattern of n, n minus 1, n minus 2, and so on.
Concept
Sometimes you don't need a whole list in order, you just need to know one specific rank, like the smallest item, the largest, or the middle (median) one.
This is the selection problem: given a list and a target rank k, find the item that would sit in position k if the list were fully sorted.
Intuition
Think of finding the median test score in a class. You don't care which student scored 2nd or 15th, you only care about the one score sitting in the middle position.
Fully sorting the whole class list would answer that question, but it would also answer a hundred questions nobody asked. Selection aims to answer only the one question that was actually asked.
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student needs the 5th smallest item out of 1,000 unsorted numbers. Their plan: sort the whole list first, then read off the item in position 5.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: Fully sorting costs an n log n amount of work to determine the order of every single item, when only one specific rank was ever asked for.
Selection never needs the full order, only enough partitioning to isolate the one target rank.
Why: Fully sorting costs an n log n amount of work to determine the order of every single item, when only one specific rank was ever asked for.
Trap
A student needs the 5th smallest item out of 1,000 unsorted numbers. Their plan: sort the whole list first, then read off the item in position 5.
This works, but it does far more work than the question requires.
Sort all 1,000 items to answer a question about just one of them
Why: Fully sorting costs an n log n amount of work to determine the order of every single item, when only one specific rank was ever asked for.
Selection never needs the full order, only enough partitioning to isolate the one target rank.
Quickselect (covered next) answers exactly this kind of question directly, without fully sorting.
Partition around a pivot and recurse into only the piece containing the target rank
Why: The moment a partition tells you which side the target rank lands on, the other side can be thrown away entirely, its internal order was never asked for.
Break the constraint
Discussion prompt
The rule this trap just fixed:
The moment a partition tells you which side the target rank lands on, the other side can be thrown away entirely, its internal order was never asked for.
Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?
Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.
Answer:
Fully sorting costs an n log n amount of work to determine the order of every single item, when only one specific rank was ever asked for.
Concept
Quickselect finds the k-th smallest item using the same partition step as quicksort, but after each partition it looks at where the pivot landed compared to the target rank k.
If the pivot's position is exactly k, the pivot is the answer, done. If the pivot's position is past k, the answer must be somewhere in the smaller-side piece, so recurse only there. If the pivot's position is before k, recurse only into the bigger-side piece, adjusting k to account for the items already ruled out.
Concept
Quickselect is quicksort with one line deleted. Because it only wants one item, it can throw away the side that cannot contain it instead of sorting both.
QUICKSELECT(A, lo, hi, k)
if lo == hi
return A[lo]
p = PARTITION(A, lo, hi)
if k == p
return A[p]
else if k < p
return QUICKSELECT(A, lo, p - 1, k)
else
return QUICKSELECT(A, p + 1, hi, k)Compare this with quicksort, which recurses on lines 8 and 10 both. Quickselect picks exactly one of them. Two calls give n log n; one call gives linear time on average, because the work halves each round instead of doubling.
Notation
Every line of QUICKSELECT says one thing. Read the line, then read what it does — not the other way round.
Annotate
Invariant
The live range only ever shrinks, and the item of rank k is always inside it. When the range narrows to one item, that item is the answer.
Step through it
At each recursion, say which half is being thrown away and why it cannot hold the answer.
Picture it
Animation
Shows: QUICKSELECT executing: the current line of pseudocode is highlighted while the data it touches changes.
Rendered with Manim.
Takeaway: Partition, then recurse into one side only — the discarded half is why quickselect averages linear time instead of n log n.
Picture it
Animation
Shows: Quickselect: recurse into one side only — a rendered Manim animation.
Rendered with Manim.
Takeaway: Discarding one side collapses the log factor entirely.
Intuition
Quicksort keeps both pieces after a partition because it eventually needs everything in order. Quickselect only ever needs one specific rank.
Once a partition shows which side the target rank must be on, the other side is guaranteed not to contain the answer, its items are all provably too small or too big. There is nothing left to gain by sorting it, so quickselect never even looks at it again.
Hypothesis
Predict first
Worked example: find the 3rd smallest with quickselect is about to be worked. State your hypothesis first: which rule or definition decides this one, and what is the first move it forces? Then watch whether the example agrees with you.
Correct: Partition the whole list around pivot 5, as before
Why: The partition gives 3, 4, 2 (positions 1 to 3), then pivot 5 at position 4, then 9, 7, 8 (positions 5 to 7).
A hypothesis you wrote down is falsifiable; a vague sense of how it will go is not. If the example opens somewhere else, that gap is the thing worth chasing.
Worked example
Find the 3rd smallest item in 8, 3, 7, 4, 9, 2, 5, counting positions starting at 1.
Partition the whole list around pivot 5, as before
Why: The partition gives 3, 4, 2 (positions 1 to 3), then pivot 5 at position 4, then 9, 7, 8 (positions 5 to 7).
Compare the pivot's position (4) to the target rank (3)
Why: Since 3 is less than the pivot's position 4, the 3rd smallest item must be inside the smaller-side piece, 3, 4, 2, the bigger-side piece can be discarded entirely.
| step | current piece | pivot | pivot's position | next move |
|---|---|---|---|---|
| 1 | 8, 3, 7, 4, 9, 2, 5 | 5 | 4 | target 3 < 4, recurse into 3, 4, 2 |
| 2 | 3, 4, 2 | 2 | 1 (within this piece) | target 3 > 1, recurse into 4, 3, new target is rank 2 within this piece |
| 3 | 4, 3 | 3 | 1 (within this piece) | target 2 > 1, recurse into remaining single item, 4 |
Partition 3, 4, 2 around pivot 2
Why: 2 is smallest, so it lands at position 1 within this piece, leaving the piece 4, 3 after it. The target rank (3) is past position 1, so recurse into 4, 3, now looking for the item at position 2 within that piece.
Partition 4, 3 around pivot 3
Why: 3 is smaller than 4, so 3 lands at position 1 within this piece, leaving the single item 4 after it. The remaining target position (2) is past position 1, so the answer is the last remaining item, 4.
Verify by checking against the fully sorted list
Why: The full sorted order of 8, 3, 7, 4, 9, 2, 5 is 2, 3, 4, 5, 7, 8, 9. The 3rd item in that list is 4, matching what quickselect found, without ever sorting the 9, 7, 8 piece.
Picture it
Animation
Shows: Each line of the worked example "find the 3rd smallest with quickselect", appearing one at a time.
The same working the example does, in the order a tutor would write it.
Takeaway: The full sorted order of 8, 3, 7, 4, 9, 2, 5 is 2, 3, 4, 5, 7, 8, 9. The 3rd item in that list is 4, matching what quickselect found, without ever sorting the 9, 7, 8 piece.
Concept
Each call to quickselect partitions its current piece (a cost proportional to that piece's size), then throws away one whole side and recurses only into the other.
On average, that surviving piece is a constant fraction smaller than the one before it, not the same size, and not just one item smaller. A sum of steadily shrinking piece sizes adds up to only a constant multiple of the original size, giving expected linear total time.
Ranking
Put in order
Put the moves of Worked example: why a shrinking sum gives linear time into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. Starting at 16 and roughly halving each time: 16, then 8, then 4, then 2, then 1.
Worked example
Suppose, as an illustration, quickselect's surviving piece is on average about half the size of the one before it. Trace the total work for an original piece of size 16.
List the piece size at each recursive call
Why: Starting at 16 and roughly halving each time: 16, then 8, then 4, then 2, then 1.
| call | piece size | work done this call |
|---|---|---|
| 1 | 16 | 16 |
| 2 | 8 | 8 |
| 3 | 4 | 4 |
| 4 | 2 | 2 |
| 5 | 1 | 1 |
Add up the work across all calls
Why: 16 plus 8 plus 4 plus 2 plus 1 is 31 total.
\[ 16+8+4+2+1 = 31 \]
Verify the total stays under twice the original size
Why: 31 is less than 32, which is twice the original piece size of 16. This is the hallmark of a shrinking geometric sum: no matter how many terms you add, the total never exceeds a small constant multiple of the first term, which is exactly why the total work stays linear in the original size.
\[ 31 < 2(16) = 32 \]
Concept
The same bad-pivot scenario that hurts quicksort hurts quickselect too: a fixed pivot rule that always picks an extreme item, on an input built to exploit it.
If every partition only rules out one item instead of a real fraction of the piece, quickselect ends up scanning nearly the whole list at every single call, adding up to the same quadratic total as quicksort's worst case.
Picture it
Animation
Shows: Why one-sided recursion is linear — a rendered Manim animation.
Rendered with Manim.
Takeaway: The geometric series is what removes the log entirely.
Step zero
Discussion prompt
Worked example: quickselect's worst case on sorted input — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Partition 1, 2, 3, 4, 5 around pivot 1
Answer:
Worked example
Find the 5th (largest) item of the sorted list 1, 2, 3, 4, 5 using quickselect with a pivot rule that always picks the first item.
Partition 1, 2, 3, 4, 5 around pivot 1
Why: 1 is smallest, landing at position 1. The target rank, 5, is past position 1, so recurse into the remaining piece, 2, 3, 4, 5, with the target still at relative rank 4 within it.
| call | piece size | pivot position | comparisons made |
|---|---|---|---|
| 1 | 5 | 1 | 4 |
| 2 | 4 | 1 | 3 |
| 3 | 3 | 1 | 2 |
| 4 | 2 | 1 | 1 |
Repeat: each partition again picks the smallest item as pivot
Why: Every call rules out exactly one item, the pivot, and recurses into everything else, never skipping any comparisons.
Verify the total comparisons match the quadratic pattern
Why: 4 plus 3 plus 2 plus 1 is 10 comparisons, the same n times (n minus 1) divided by 2 total as quicksort's worst case on this exact input, because quickselect never got to discard a large piece the way it should.
\[ 4+3+2+1 = 10 = \frac{5(5-1)}{2} \]
Invariant
Step through it
Step through Worked example: quickselect's worst case on sorted input one row at a time. One of these columns never changes — find it, and say why it cannot.
Prediction
Predict first
Which approach finds the answer fastest on average, and why?
Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.
Correct: Quickselect: partition and recurse into only the side containing the target rank, discarding the other side entirely.
Why: Quickselect needs the same partition step as quicksort, but after each partition it can discard the entire side that cannot contain the target rank. On average this halves-or-better the remaining work each call, giving expected linear time overall, faster than fully sorting.
Check
You need to find the 5th smallest number in an unsorted list of 1,000 numbers, and nothing else about the order of the other numbers matters.
Check your understanding
Which approach finds the answer fastest on average, and why?
Answer: A
Why: Quickselect needs the same partition step as quicksort, but after each partition it can discard the entire side that cannot contain the target rank. On average this halves-or-better the remaining work each call, giving expected linear time overall, faster than fully sorting.
Concept
A random pivot makes quickselect's worst case wildly unlikely, but does not rule it out. Some applications, like real-time systems, need a running time that is guaranteed to be linear, every single time, no exceptions.
That calls for a pivot rule that is chosen deliberately, not randomly, and that can be proven to always produce a good-enough split.
Intuition
Earlier, a "good" pivot was one landing in the middle half of the piece. A random pivot only lands there about half the time.
What if, instead of hoping, we built a small procedure that always picks a pivot guaranteed to be reasonably central, one that can never be tricked into landing on an extreme item, no matter what the input looks like?
Concept
The median-of-medians pivot rule starts by chopping the whole piece into small groups of five items each (the very last group may have fewer than five if the piece's size doesn't divide evenly).
A piece of size n is split into roughly n divided by 5 such groups.
Concept
Step 2: find the median of each group of five. Since every group only has five items, finding its median is cheap, just a small, fixed number of comparisons, no matter how big the whole piece is.
Step 3: collect all of those group medians into their own small list, then recursively find the median of that list. Use that value as the pivot for the main piece.
Picture it
Animation
Shows: Median of medians guarantees a decent pivot — a rendered Manim animation.
Rendered with Manim.
Takeaway: A worse pivot than random, but with a guarantee attached.
Estimation
Predict first
Find the median-of-medians pivot for the 15-item list 12, 3, 17, 5, 9, 20, 1, 14, 8, 22, 6, 11, 19, 2, 15.
Commit before you compute: what does Worked example: computing a median-of-medians pivot come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: Verify how balanced a split this pivot actually produces
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. Checking all 15 items against 11: seven items (3, 5, 9, 1, 8, 6, 2) are less than 11, one item equals 11, and seven items (12, 17, 20, 14, 22, 19, 15) are greater than 11.
Worked example
Find the median-of-medians pivot for the 15-item list 12, 3, 17, 5, 9, 20, 1, 14, 8, 22, 6, 11, 19, 2, 15.
Split the 15 items into 3 groups of five
Why: Group A: 12, 3, 17, 5, 9. Group B: 20, 1, 14, 8, 22. Group C: 6, 11, 19, 2, 15.
Find the median of each group
Why: Sorting each group of five: A sorts to 3, 5, 9, 12, 17 (median 9). B sorts to 1, 8, 14, 20, 22 (median 14). C sorts to 2, 6, 11, 15, 19 (median 11).
| group | items | sorted | median |
|---|---|---|---|
| A | 12, 3, 17, 5, 9 | 3, 5, 9, 12, 17 | 9 |
| B | 20, 1, 14, 8, 22 | 1, 8, 14, 20, 22 | 14 |
| C | 6, 11, 19, 2, 15 | 2, 6, 11, 15, 19 | 11 |
Find the median of the three group medians
Why: The medians are 9, 14, and 11. Sorted, that's 9, 11, 14, the median of these three is 11.
\[ \text{median}(9, 14, 11) = 11 \]
Verify how balanced a split this pivot actually produces
Why: Checking all 15 items against 11: seven items (3, 5, 9, 1, 8, 6, 2) are less than 11, one item equals 11, and seven items (12, 17, 20, 14, 22, 19, 15) are greater than 11. A 7-and-7 split out of 14 remaining items is close to perfectly even, far better than the guarantee alone would require.
Comparison
Comparison matrix
From Worked example: computing a median-of-medians pivot: refill the median column from what you know. The rest of the table is as it appeared.
| group | items | sorted | median |
|---|---|---|---|
| A | 12, 3, 17, 5, 9 | 3, 5, 9, 12, 17 | 9 |
| B | 20, 1, 14, 8, 22 | 1, 8, 14, 20, 22 | 14 |
| C | 6, 11, 19, 2, 15 | 2, 6, 11, 15, 19 | 11 |
Intuition
Two competing needs shape the group size. Smaller groups mean finding each group's median is even cheaper, but a size guarantee built from tiny groups turns out to be weaker (more on that in the next few slides).
Larger groups give a stronger guarantee, but sorting each group to find its median costs more comparisons per group. Five items is the smallest group size that still produces a guarantee strong enough to make the whole algorithm run in linear time, which is exactly why the trap on the next slide matters.
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student remembers that the groups must have an odd number of items so that each group has a single, unambiguous median, and concludes that any odd size, 3, 5, 7, 9, would work equally well, with 5 being just a traditional pick.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: Groups of three are odd, so each has a clear middle item, this satisfies the odd-size reasoning, but misses the real requirement.
Oddness explains why every group needs a single median, but it does not explain why five specifically was chosen over three, seven, or nine.
Why: Groups of three are odd, so each has a clear middle item, this satisfies the odd-size reasoning, but misses the real requirement.
Trap
A student remembers that the groups must have an odd number of items so that each group has a single, unambiguous median, and concludes that any odd size, 3, 5, 7, 9, would work equally well, with 5 being just a traditional pick.
Try groups of three instead of five
Why: Groups of three are odd, so each has a clear middle item, this satisfies the odd-size reasoning, but misses the real requirement.
Oddness explains why every group needs a single median, but it does not explain why five specifically was chosen over three, seven, or nine.
The real reason involves the recurrence the pivot rule produces, covered fully in the next few slides. Groups of three give a size guarantee too weak to prove linear time; groups of five are the smallest size for which the guarantee is strong enough.
Compare the fraction of guaranteed elements for groups of three versus groups of five
Why: With groups of three, the resulting recurrence's fractions add up to exactly one, not strictly less than one, so the standard argument for linear time does not go through. With groups of five, the fractions add up to nine tenths, strictly below one, which is exactly what makes the linear-time proof work.
\[ \text{groups of 3: } \frac{1}{3}+\frac{2}{3}=1 \qquad \text{groups of 5: } \frac{1}{5}+\frac{7}{10}=\frac{9}{10}<1 \]
Two truths and a lie
Sort into buckets
Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.
Concept
Before proving anything, name exactly what is being counted. Call n the total number of items in the piece, and call the chosen pivot the median-of-medians value.
The guarantee to prove has two parts: some fraction of all n items are provably less than or equal to the pivot, and that same fraction are provably greater than or equal to it.
\[ \text{at least } \tfrac{3n}{10} \text{ items} \le \text{pivot}, \qquad \text{at least } \tfrac{3n}{10} \text{ items} \ge \text{pivot} \]
Intuition
Watch me not know the answer. This is what the first two minutes actually look like.
We need a guarantee that the pivot is never too close to an end. Groups of five, medians taken, then the median of those medians.
Try to prove the pivot is the true median, or close to it
Why: If we could show the pivot lands near position n/2, the recurrence would be the balanced one and we would be done.
It is not close to the median, and it cannot be made to be
Why: Medians of medians can sit well away from the true median. Chasing a tight bound here is chasing something false, and no amount of algebra rescues it.
Dead end. Not a mistake — a move that was worth trying and did not pay off. This happens in most proofs.
Back up. Ask for much less: a constant fraction, not the middle
Why: We do not need near-half. We only need to throw away some fixed fraction every time. Count the elements guaranteed to be on each side and take whatever bound falls out.
\[ \text{at least } \; \tfrac{3}{10}n \; \text{ elements are guaranteed on each side} \]
So the recursive call is on at most 7n/10 elements. Weak, ugly, and completely sufficient. Aiming for the weakest bound that still works is a skill, not a compromise.
The expert does not see the whole path in advance. The expert tries something, reads the result, and adjusts. That is the skill.
Picture it
Animation
Shows: Where the 30 percent comes from — a rendered Manim animation.
Rendered with Manim.
Takeaway: A guarantee, bought with a much larger constant.
Intuition
Start with the groups whose own median is less than or equal to the overall median-of-medians pivot. Since the pivot is itself the median of all the group medians, at least half of the groups have a median in this category.
Now look inside just one of those qualifying groups. It has five items, and its median is less than or equal to the pivot. Within that one group, the median itself plus the two items smaller than it, three items total, are also all less than or equal to the pivot.
Multiply that out: at least half of the groups, each contributing at least three qualifying items, gives a guaranteed count of items less than or equal to the pivot across the whole piece. The exact same argument, mirrored, guarantees the same count for items greater than or equal to the pivot.
Estimation
Predict first
Apply the counting argument to a piece of 40 items, split into groups of five.
Commit before you compute: what does Worked example: verifying the count for n = 40 come out to? A rough magnitude and the right form is enough — the point is to have something concrete to be wrong about.
Correct: Verify this matches the claimed fraction
Why: A prediction you can defend turns the computation into a check rather than a leap of faith — and an answer that contradicts it is caught on the spot. 3n divided by 10 for n equal to 40 is 12, exactly the count just derived from counting groups and items directly, confirming the guarantee holds for this concrete case.
Worked example
Apply the counting argument to a piece of 40 items, split into groups of five.
Find the number of groups
Why: 40 items divided into groups of five gives 8 groups total.
\[ \frac{40}{5} = 8 \text{ groups} \]
Find how many of those groups have a median less than or equal to the pivot
Why: At least half of the 8 groups qualify, since the pivot is the median of all 8 group medians.
\[ \frac{8}{2} = 4 \text{ qualifying groups} \]
Count guaranteed items from those qualifying groups
Why: Each qualifying group contributes at least 3 items (its median plus the two items below it) that are also less than or equal to the overall pivot.
\[ 4 \times 3 = 12 \text{ items} \le \text{pivot} \]
Verify this matches the claimed fraction
Why: 3n divided by 10 for n equal to 40 is 12, exactly the count just derived from counting groups and items directly, confirming the guarantee holds for this concrete case.
\[ \frac{3(40)}{10} = 12 \ \checkmark \]
Picture it
Animation
Shows: Each line of the worked example "verifying the count for n = 40", appearing one at a time.
The same working the example does, in the order a tutor would write it.
Takeaway: 3n divided by 10 for n equal to 40 is 12, exactly the count just derived from counting groups and items directly, confirming the guarantee holds for this concrete case.
Concept
If at least three tenths of the n items are guaranteed to be less than or equal to the pivot, and the mirrored argument guarantees the same fraction is greater than or equal to it, then neither side of the partition can hold more than the remaining seven tenths.
\[ n - \frac{3n}{10} = \frac{7n}{10} \]
No matter how the rest of the items happen to fall, the larger recursive call, the one a worst-case adversary would want to make as big as possible, is capped at seven tenths of n. This is a guarantee, not an average.
Concept
Selecting with this pivot rule involves three costs: recursively finding the median of the roughly n divided by 5 group medians, recursively selecting within the larger side of the partition (at most seven tenths of n), and the work of grouping, finding each group's median, and partitioning, all proportional to n.
\[ T(n) = T\!\left(\frac{n}{5}\right) + T\!\left(\frac{7n}{10}\right) + O(n) \]
Both recursive pieces are strictly smaller than n, and their sizes are both guaranteed, not just typical or average.
Picture it
Animation
Shows: Recurse into the smaller side first — a rendered Manim animation.
Rendered with Manim.
Takeaway: A one-line change that removes the stack-overflow risk entirely.
Intuition
Add up the two recursive fractions: one fifth plus seven tenths equals nine tenths.
\[ \frac{1}{5} + \frac{7}{10} = \frac{9}{10} \]
Since nine tenths is strictly less than one whole, the two recursive calls together always cover somewhat less total work than the level above them, the total work per level shrinks by a constant factor as the recursion goes deeper, the same shrinking-sum pattern that made quickselect's average case linear.
Intuition
What move should we make next?
The median-of-medians recurrence, with both recursive calls:
\[ T(n) \le T(n/5) + T(7n/10) + O(n) \]
\[ \frac{1}{5} + \frac{7}{10} = \frac{9}{10} < 1 \]
You solved a recurrence with exactly this shape two lessons ago. Which move, and what do you expect the per-level work to do?
_Look at your toolkit. Say a move number out loud before this slide advances._ A wrong guess is useful. A silent guess is not.
Step zero
Discussion prompt
Worked example: verifying the recurrence resolves to linear time — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Find the total size handed to the next level of recursion
Answer:
Worked example
Illustrate the shrinking-sum pattern starting from a piece of size 1,000.
Find the total size handed to the next level of recursion
Why: The two recursive pieces have sizes n divided by 5 and 7n divided by 10; together they add up to nine tenths of n.
\[ \frac{1000}{5} + \frac{7(1000)}{10} = 200 + 700 = 900 \]
Repeat for a few more levels
Why: Each level's total size is nine tenths of the level above: 1000, then 900, then 810, then 729, and so on, shrinking by the same constant factor every time.
| level | total size at this level |
|---|---|
| 0 | 1000 |
| 1 | 900 |
| 2 | 810 |
| 3 | 729 |
Add up the per-level costs
Why: Total work is proportional to 1000 plus 900 plus 810 plus 729 and so on, a shrinking geometric sum with ratio nine tenths, which converges to a bounded constant multiple of 1000, exactly like the earlier quickselect sum converged to a constant multiple of its starting size.
Verify the total stays within a constant multiple of the original size
Why: A geometric sum with a ratio strictly less than one and first term proportional to n always totals to at most a fixed constant times n, here, that constant works out to 10, since the sum of nine-tenths raised to every power converges to 10. So the total work is proportional to n, confirming linear worst-case time.
\[ 1000 + 900 + 810 + 729 + \cdots \le 10(1000) \]
Picture it
Animation
Shows: Each line of the worked example "verifying the recurrence resolves to linear time", appearing one at a time.
The same working the example does, in the order a tutor would write it.
Takeaway: A geometric sum with a ratio strictly less than one and first term proportional to n always totals to at most a fixed constant times n, here, that constant works out to 10, since the sum of nine-tenths raised to every power converges to 10. So the total work is proportional to n, confirming linear worst-case time.
Concept
The move: #11 (Unroll and sum the levels), then #3 (Drop the tail).
Unroll and look at the per-level work
Why: Level 0 does n. Level 1 does 9n/10. Level 2 does 81n/100. Each level is 9/10 of the one above it.
\[ n + \tfrac{9}{10}n + \left(\tfrac{9}{10}\right)^{2}n + \cdots = \frac{n}{1 - 9/10} = 10n \]
Drop the tail to see it is really linear
Why: The series is dominated by its first term. All the levels below the root together add up to at most nine times the root — a constant multiple, so the whole thing is Theta of n.
If the fractions had summed to exactly 1, every level would cost n and you would get n log n. Above 1, the leaves dominate and it blows up. That one comparison decides the answer, and it is the same comparison the Master Theorem makes.
Notation
Annotate
From Why the fractions adding to less than one is the whole game — read this one piece at a time. What is each part doing?
On: \( n + \tfrac{9}{10}n + \left(\tfrac{9}{10}\right)^{2}n + \cdots = \frac{n}{1 - 9/10} = 10n \)
Concept
Because the pivot is chosen deliberately rather than at random, the seven-tenths bound on the larger recursive call holds for every single input, not just on average.
\[ T(n) = O(n) \text{ in the worst case, guaranteed} \]
This closes the gap left by quickselect: quickselect is linear on average but quadratic on some adversarial inputs; median-of-medians is linear on every input, including the ones built to defeat a naive pivot rule.
Picture it
Animation
Shows: Finding the k-th does not need a sort — a rendered Manim animation.
Rendered with Manim.
Takeaway: Sorting answers a much harder question than the one asked.
Intuition
"Linear time" hides a constant factor, and median-of-medians has a noticeably larger one, grouping the whole piece, sorting every group of five, and recursing twice per call all add real overhead that a simple random pivot never pays.
In everyday code, where the input isn't hand-crafted to attack a specific pivot rule, randomized quickselect's expected linear time with a small constant usually beats median-of-medians' guaranteed linear time with a larger one.
Concept
Use randomized quickselect when average-case speed is what matters and an astronomically unlikely bad case is an acceptable risk, which describes most everyday selection tasks.
Use median-of-medians (or run quickselect with a median-of-medians pivot only when needed) when the running time must be bounded no matter what, such as in a system where a worst-case guarantee is a hard requirement, not just a nice-to-have.
Explain it
Discussion prompt
Explain Choosing between quickselect and median-of-medians to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
Use randomized quickselect when average-case speed is what matters and an astronomically unlikely bad case is an acceptable risk, which describes most everyday selection tasks.
Picture it
Animation
Shows: Choosing between them — a rendered Manim animation.
Rendered with Manim.
Takeaway: Worst-case guarantees versus practical speed.
Ranking
Put in order
Put the moves of Worked example: median-of-medians selection end to end into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. The groups of five gave medians 9, 14, and 11, and the median of those three medians is 11, that is this call's pivot.
Worked example
Find the median (the 8th smallest of 15 items) of the same list from before: 12, 3, 17, 5, 9, 20, 1, 14, 8, 22, 6, 11, 19, 2, 15.
Reuse the median-of-medians pivot already computed
Why: The groups of five gave medians 9, 14, and 11, and the median of those three medians is 11, that is this call's pivot.
Partition all 15 items around pivot 11
Why: As found earlier, 7 items are less than 11, 1 item equals 11 (the pivot itself), and 7 items are greater than 11, pivot 11 lands at position 8 in the fully partitioned order.
| quantity | value |
|---|---|
| items less than pivot | 7 |
| pivot's position | 8 |
| items greater than pivot | 7 |
| target rank | 8 |
Compare the pivot's position (8) to the target rank (8)
Why: They match exactly, so the pivot itself is the answer, no further recursion is needed on either side.
Verify by checking against the fully sorted list
Why: Sorting all 15 items gives 1, 2, 3, 5, 6, 8, 9, 11, 12, 14, 15, 17, 19, 20, 22. The 8th item in that list is 11, matching the pivot found, and it took just one partition, since the median-of-medians pivot happened to land exactly on the target rank this time.
Discrimination
Sort into buckets
Sort these by value, from memory, without looking back at Worked example: median-of-medians selection end…. Telling them apart on the spot is the skill; the table is only where the answer happens to be written down.
Check
The median-of-medians pivot rule splits the piece into groups of five items each.
Check your understanding
Why does using groups of five (rather than groups of three) guarantee linear worst-case running time?
Answer: A
Why: The size guarantee groups of five produce leads to the recurrence T(n) = T(n/5) + T(7n/10) + O(n), and one fifth plus seven tenths is nine tenths, strictly below one. Groups of three instead lead to fractions that add to exactly one, which is not strictly below one and does not give the same linear-time guarantee.
Elimination
Eliminate the wrong options
The median-of-medians pivot is guaranteed to have at least how many of the 40 items less than or equal to it?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: At least half of the 8 groups (4 groups) have a median less than or equal to the overall pivot, and each such group contributes at least 3 items (its median plus the two items below it) that are also less than or equal to the pivot, 4 times 3 is 12, matching 3n divided by 10 for n equal to 40.
Check
A piece has 40 items, split into 8 groups of five for the median-of-medians pivot rule.
Check your understanding
The median-of-medians pivot is guaranteed to have at least how many of the 40 items less than or equal to it?
Answer: A
Why: At least half of the 8 groups (4 groups) have a median less than or equal to the overall pivot, and each such group contributes at least 3 items (its median plus the two items below it) that are also less than or equal to the pivot, 4 times 3 is 12, matching 3n divided by 10 for n equal to 40.
Concept
Many production sorting libraries don't choose purely between plain quicksort and a worst-case-guaranteed method, they combine ideas from both.
A common strategy: run ordinary randomized quicksort for its fast average case, but keep track of how deep the recursion goes. If the recursion ever runs suspiciously deep, a sign of an unlucky or adversarial input, switch over to a method with a guaranteed running time for the rest of the work, so the total never blows up to quadratic.
Analogy
Discussion prompt
Explain Real-world sorts hedge their bets by analogy to something with no CS3000 Algorithms in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
Many production sorting libraries don't choose purely between plain quicksort and a worst-case-guaranteed method, they combine ideas from both.
Picture it
Animation
Shows: Quicksort sorts in place — a rendered Manim animation.
Rendered with Manim.
Takeaway: The memory advantage that keeps it in standard libraries.
Concept
Moves added today: none.
That is a result, not a gap. Everything in this lesson was proved with moves you already owned.
Moves you reused today:
Move #2 showed up in a new costume today: finding the input that forces quicksort's worst case is exactly building the value that breaks a claimed bound.
Full toolkit so far: #1 through #11.
Next session opens with you naming every one of these from memory, before any new material.
Counterexample
Discussion prompt
Move #2 showed up in a new costume today: finding the input that forces quicksort's worst case is exactly building the value that breaks a claimed bound.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Answer:
Next session opens with you naming every one of these from memory, before any new material.
Connect it up
Draw it
One page, no notation unless you need it: draw how these connect — How to reason about quicksort's running time · Toolkit check-in: name them before you look · Quicksort in one sentence · Splitting one pile into two · What it means for a list to be "partitioned". Put an arrow wherever one of them is what makes another possible, and label the arrow with why.
Recap
Quicksort, quickselect, and median-of-medians all lean on the same partition step, applied with different goals.
| Algorithm | Goal | Guaranteed worst case |
|---|---|---|
| Quicksort | Fully sort the list | Quadratic with a fixed, exploitable pivot rule |
| Randomized quicksort | Fully sort the list | Quadratic in principle, but astronomically unlikely |
| Quickselect | Find one rank | Linear on average; quadratic with a fixed, exploitable pivot rule |
| Quickselect with median-of-medians | Find one rank | Linear, guaranteed on every input |
The pivot rule is never a minor detail, it is the single choice that decides whether an algorithm's worst case is a real risk or an all-but-impossible coincidence.
Want this taught 1-on-1? Alexander tutors CS3000 Algorithms — $55/session, free consultation.