Exercises — Module 06 (Sorting)
Self-contained problems on this module's material — inversions, straight insertion, Shellsort's -ordering, quicksort's average cost, heap construction, natural merge sort, the comparison lower bound, and LSD radix sort. You can work every one without the books: each states the problem in full, gives a hint to peek at when stuck, and a worked answer sketch to check against after you try. Computational answers here are reproduced by the code you write in the lab (or a few lines at a REPL).
Ratings follow Knuth's scale (00–50; M = needs mathematics, ▶ = especially
instructive). Where a problem mirrors a TAOCP exercise its number is noted for
readers who own Volume 3.
Tracking
| # | Topic | Rating | Status |
|---|---|---|---|
| 1 | Reversed permutation attains the maximum inversions | 15 | ⬜ |
| 2 | ▶ Inversions equal straight-insertion moves | 22 | ⬜ |
| 3 | An -ordered file stays -ordered after a -sort | 20 | ⬜ |
| 4 | ▶ Derive quicksort's average comparison count | 24 | ⬜ |
| 5 | make_heap uses fewer than comparisons | 22 | ⬜ |
| 6 | Natural merge takes passes on runs | 20 | ⬜ |
| 7 | ▶ Stirling gives | 25 | ⬜ |
| 8 | LSD radix sort correct by induction on digit position | 20 | ⬜ |
Problems
1. Reversed permutation attains the maximum inversions (rating 15 · cf. 5.1.1-2)
Problem. An inversion of a permutation is a pair of positions with . Show that the fully reversed permutation has inversions, and that no permutation can have more.
Hint. How many pairs with are there in total? For the reversed sequence, which of them are inverted?
Answer sketch. There are exactly position pairs , and each is either an inversion or not — so is a hard upper bound for any permutation. In the reversed sequence is strictly decreasing, so every pair satisfies : all pairs are inversions, meeting the bound. (Verified: gives , the value the lab asserts for a reversed length-100 array scaled down — the reference pins at .) This maximum is exactly why straight insertion is worst on reverse-sorted input.
2. ▶ Inversions equal straight-insertion moves (rating 22 · cf. 5.1.1-8)
Problem. Straight insertion sort (Algorithm S) inserts into the sorted prefix by shifting larger keys one slot to the right (step S4). Prove that the total number of S4 shifts over the whole sort equals the inversion count of the input.
Hint. Fix the step that inserts . Which earlier elements get shifted, and how do they relate to inversions ending at position ? Nothing outside step moves during step .
Answer sketch. When inserting , step S4 shifts right exactly those records () whose key exceeds — i.e. exactly the pairs that form an inversion with second coordinate . Each is moved once and nothing else moves during step . Summing over , the total moves equal the total number of inverted pairs, namely . (Verified on Knuth's sixteen numbers: the per-step moves , and a direct inversion count also gives .) This is the model example of "analyze an algorithm by finding the combinatorial quantity it is really counting," and it immediately explains insertion sort's average (since the average inversion count is ) and its linearity on nearly-sorted input.
3. An -ordered file stays -ordered after a -sort (rating 20 · cf. 5.2.1-14)
Problem. A file is -ordered if for every valid . Shellsort works by -sorting for a decreasing sequence of increments. Prove the lemma that makes the decreasing sequence sound: if a file is -ordered, then after -sorting it (for any increment ) it is still -ordered — so later passes never undo earlier ones.
Hint. A -sort sorts each of the subfiles taken at stride independently. Compare the file before and after against a known combinatorial fact: sorting corresponding subsequences preserves an existing " at distance " relation. Think of the elements at positions and and where they land after the -sort.
Answer sketch. This is a special case of the fact that if two sequences and satisfy for all , and each is then sorted, the pointwise inequality survives. In the file, partition into stride- subfiles; the -sort sorts each subfile. Consider any : the -ordering , applied across all positions, means the sorted subfiles remain pointwise dominated at offset , so still holds afterward. Hence the file stays -ordered. (The lab checks a concrete instance from the sequence: after 13-sorting then 4-sorting, the file is both 4-ordered and still 13-ordered.) Because finer orderings accumulate instead of fighting, a decreasing increment sequence makes monotone progress toward sorted, which is the whole reason Shellsort works.
4. ▶ Derive quicksort's average comparison count (rating 24 · cf. 5.2.2-13)
Problem. Let be the expected number of key comparisons quicksort makes on a random permutation of distinct keys (take cutoff , so no insertion pass). Partitioning a size- segment costs comparisons and leaves the pivot equally likely to be the th smallest. Set up and solve the recurrence for in closed form, and give its leading asymptotic term.
Hint. Start from
Multiply by , write the same relation for , subtract to eliminate the sum, then divide by and telescope. The partial fraction is the key algebra.
Answer sketch. Multiplying by and subtracting the version kills the sum:
Dividing by and using the partial fraction gives
Telescoping from yields , so the exact closed form is
where . (Verified against the raw recurrence: , , , both formulas agreeing exactly.) Since ,
about 39% above the information-theoretic floor (Problem 7). Median-of-three pivoting lowers the constant to about .
5. make_heap uses fewer than comparisons (rating 22 · cf. 5.2.3-20)
Problem. make_heap turns an arbitrary array into a max-heap by sifting each
internal node, from index down to . Sifting a node of height
above the leaves costs at most comparisons (two per level descended). Prove
that the whole build costs fewer than comparisons — it is linear, not
.
Hint. Count how many nodes sit at each height in a complete binary tree of nodes: at most . Then sum times that over all heights, and use the closed form of at .
Answer sketch. At most nodes have height , each costing comparisons, so the total is at most
Using at , the inner sum
is . (Verified numerically: .)
Hence the build costs at most comparisons — linear. The intuition: sifting
is cheap for the many short subtrees near the leaves and expensive only for the
one tall root, so the cost is dominated by the abundant cheap cases. (The lab
pins the max at the root after make_heap — 908 for the sixteen numbers.) This
is why heapsort's comes entirely from the extract-max
siftups, not from building the heap.
6. Natural merge takes passes on runs (rating 20 · cf. 5.2.4-12)
Problem. A run is a maximal non-decreasing stretch; natural merge sort detects the existing runs and merges them pairwise each pass, repeating until one run remains. Show that on an input with runs the sort finishes in at most passes, each costing comparisons, for total . What does this give for already-sorted input?
Hint. How does the run count change across one pass that merges runs in pairs? Set up the recurrence for the number of runs remaining and count how many halvings reach .
Answer sketch. Each pass pairs up adjacent runs and merges each pair into one, so runs become at most runs. Starting from , after passes at most runs remain; this reaches once , i.e. after passes. Each pass scans the whole file once, comparisons, so the total is . For sorted input : a single detection scan of comparisons and we are done — the sort adapts to pre-existing order (neither quicksort nor heapsort does). (Verified: the sixteen numbers split into runs, so passes; the lab confirms sorted 100k input costs exactly comparisons.)
7. ▶ Stirling gives (rating 25 · cf. 5.3.1-3)
Problem. A comparison sort is a binary decision tree with a distinct leaf for each of the orderings, so its worst-case height is at least . Use Stirling's approximation to show
and conclude that every comparison sort needs comparisons in the worst case.
Hint. Convert natural logs to base 2 by multiplying through by . The term in Stirling becomes ; the error becomes (bases differ by a constant).
Answer sketch. Multiply Stirling's by :
since . With this is . (Verified: gives vs. ; gives vs. — the gap is the term.) Because the decision tree needs leaves and a height- binary tree has leaves, : no comparison sort can beat comparisons asymptotically. Merge sort's and heapsort's are therefore optimal up to the constant.
8. LSD radix sort correct by induction on digit position (rating 20 · cf. 5.2.5-4)
Problem. LSD radix sort makes 8 passes over u64 keys, each a stable
counting sort on one byte, least-significant byte first. Prove it correctly sorts
the keys, by induction on the byte position, and identify exactly where
per-pass stability is required.
Hint. State the invariant "after the pass on byte , the array is sorted by the low bytes treated as a -byte number." For the inductive step, the pass on byte keys on byte only — what does stability guarantee about keys with equal byte ?
Answer sketch. Base (): the first stable counting sort orders keys by
byte 1. Step: assume the array is sorted by bytes . The pass on byte
is a stable counting sort keyed on byte , so keys with equal byte keep
their incoming relative order — which by hypothesis is sorted on bytes .
Afterward keys are ordered first by byte , ties broken by bytes : i.e.
sorted by the low bytes. After byte 8 the array is sorted by all 64 bits.
Stability is load-bearing at every step: an unstable per-pass sort would
scramble the ordering the earlier passes established, breaking the induction.
(The lab proves stability directly by packing (payload << 32) | original_index
and checking equal-payload records emerge in increasing index order.) Total work
is — linear, evading the comparison bound because radix
sort performs no key comparisons.
Your solutions
Use this space to log your own work — a restated problem, your approach, and how it compared with the sketch above.
Exercise N (rating RR)
Approach. Answer / proof. Compared with the sketch: what you did differently, what you missed.