3.6.3 · D1Sorting & Searching

Foundations — Quick sort — Lomuto - Hoare partition, pivot strategies, expected O(n log n), worst case

2,307 words10 min readBack to topic

Before you can read a line of that parent note, you need a vocabulary. Below is every symbol and idea it silently assumes, each built from nothing, in an order where each rung of the ladder rests on the one below it. If a term already makes total sense to you, skim — but the [!recall] checklist at the bottom is the real test.


1. An array and its indices

Figure — Quick sort — Lomuto - Hoare partition, pivot strategies, expected O(n log n), worst case

The picture: look at the strip above. The teal number under each box is its index ; the plum number inside is the value . So here is the fourth box.

Why the topic needs it: every pointer in Lomuto and Hoare (i, j, lo, hi, q) is just an index — a finger pointing at one box. If "index" is fuzzy, all the pointer-chasing is noise.


2. Subarray and the range

The picture: imagine two brackets you can slide inward around any chunk of the row. Everything between them is the subarray you are currently working on.

Why the topic needs it: quicksort is recursive — it calls itself on smaller pieces. Each call says "sort just this slice, from lo to hi." Without a way to name a slice, recursion cannot even be stated.


3. Swap — the one physical move

The picture: two boxes trade the numbers inside them, like two people swapping seats. The indices stay put; only the values travel.

Why the topic needs it: quicksort is in-place — it never builds a second array, it only rearranges the boxes it already has. The only tool for rearranging is swap. Every partition routine is a sequence of swaps.


4. The pivot and "partition"

Figure — Quick sort — Lomuto - Hoare partition, pivot strategies, expected O(n log n), worst case

The picture: three coloured zones. The orange left zone holds everything ; the single teal box is the pivot in its home ; the plum right zone holds everything . The zones are unsorted within themselves — that is what recursion fixes next.

Why the topic needs it: this is the whole engine. Once is at , the problem splits into two smaller, independent problems (left zone, right zone) that never interact again. "" is exactly the return value of Lomuto's partition.


5. Comparisons: , , ,

Why the topic needs it: partition decides which zone a value belongs to by comparing it to the pivot. The exact choice of vs is what the loop invariant pins down (e.g. "" uses strict less-than). Getting these boundaries right is the difference between correct code and an infinite loop — especially with duplicate (equal) values, which is why the parent note flags equal elements as dangerous.


6. Pointers i, j and their invariants

Figure — Quick sort — Lomuto - Hoare partition, pivot strategies, expected O(n log n), worst case

The picture: j (plum arrow) sweeps left-to-right inspecting values; i (orange arrow) lags behind, sitting at the last box already known to be . Every time j finds a small value, i steps forward and swaps it in.

Why the topic needs it: the parent's proof of correctness is its invariant (, etc.). An invariant is how you trust code without tracing every case: if it holds at the start, and each step preserves it, then it holds at the end — and the end statement is exactly "the array is partitioned." This is the logical backbone of both Lomuto and Hoare.


7. Recursion and the recursion tree

Figure — Quick sort — Lomuto - Hoare partition, pivot strategies, expected O(n log n), worst case

The picture: the balanced tree (left) halves each time → short and bushy, depth . The lopsided chain (right) peels one element per call → depth . Same algorithm, wildly different shapes depending on pivot luck.

Why the topic needs it: the entire complexity story lives in this tree. Each level does work (one partition pass over its boxes). So total work = work-per-level × depth. Bushy tree → . Chain → . That is literally where and the worst case come from.


8. Big-O and — the size labels

Why the topic needs it: the whole reason quicksort matters is its speed label. "Expected , worst " is a one-line summary of the recursion-tree analysis. Without these labels you cannot compare quicksort to Merge Sort or Heap Sort.


9. Logarithm — the depth of a bushy tree

The picture: each level of a balanced recursion tree cuts the slice in half. The number of levels until size is the number of halvings — that is . That's why a balanced quicksort tree has depth .

Why the topic needs it: the "log" in is this halving count. It is why doubling the array only adds one level of work, not double the work.


10. Sum and the harmonic number

Why the topic needs it: the parent's expected-comparison count is . If and are unfamiliar, the elegant "" derivation is unreadable. You do not need to derive here — just recognise that this friendly sum is what produces the in the average case.

Recall Why does a sum of

give a logarithm? Because adding up from to is close to the area under the curve , and that area is exactly . (Full detail lives with the Master Theorem toolkit — here just trust the shape.)


How the foundations feed the topic

Array A and index A of k

Subarray lo to hi

swap two boxes

Pivot p and partition into q

Comparisons less greater equal

Pointers i j and invariant

Recursion and recursion tree

depth uses log2 n

work per level times depth

Big-O and Theta labels

Sum sign and harmonic Hn

expected comparisons 2 n Hn

Quicksort complexity story


Equipment checklist

Test yourself — cover the right side and answer out loud.

What does mean, and why do indices start at 0?
The value in box number ; counting from 0 means an -box array uses indices .
How many boxes are in the slice ?
(both ends included).
Why does quicksort use swap instead of inserting?
Swap touches exactly two boxes (); it keeps the algorithm in-place and cheap.
After partitioning, where is the pivot ?
In its final sorted position — everything left is smaller, everything right is bigger.
What is a loop invariant?
A statement true at the top of the loop every pass; it proves the partition is correct at the end.
Why does each level of the recursion tree cost ?
One partition pass at each level scans all the boxes on that level, totalling .
What question does answer?
How many times you halve to reach 1 — i.e. the depth of a balanced tree.
Difference between and ?
is an upper bound (at most); is tight (grows exactly like ).
What is and roughly how big is it?
; it produces the log in the average-case count.

Ready? If every line above felt solid, jump to the parent note and read the Lomuto trace — every pointer will now be a familiar finger on a familiar box.