Proofs: MSD radix sort
These are the human proofs of MSD radix sort, at full length.
MSD radix sort sorts strings in lexicographic order and is stable full
This proof shows: correctness, stability, termination.
Strings are over , and for and otherwise. Write for the prefix of length (all of when ).
Fact 1. Let and have the same prefix of length , and let and . If , then in lexicographic order. If , then .
Proof. If , then and is a proper prefix of , so . If , then and is the first index where they differ. If both are 0, both have length and the same prefix of length , so they are equal.
We call a task valid if the strings of all have length at least and the same prefix of length . We call the array good for a set of tasks on if:
(G1) the ranges of the tasks are disjoint; (G2) each task is valid; (G3) for indices that are not in the same task, in lexicographic order, and if then the item at came first in the input; and the multiset of is the input; (G4) inside each task, equal strings are in input order.
Initialization. The single task is valid, since each string has the empty prefix. (G1), (G3) and (G4) hold, because there is one task and nothing has moved.
Maintenance. Pop a task . If , drop it. One index forms its own range, so (G3) now covers it with the indices of the other tasks: those are already in order with it by (G3) before the pop, since they were not in the same task. Otherwise, lines L5 to L8 stably sort the range by , and lines L9 and L10 push one task for each bucket .
- The new ranges are inside and disjoint, so (G1) holds.
- A string in bucket has length more than and the old common prefix plus the character . So each new task is valid, and (G2) holds.
- For (G3), take two indices of the old range that are not in the same new task. If they are in different buckets, the one in the lower bucket comes first after the stable sort, and Fact 1 says that its string is less. If both are in bucket 0, the strings are equal by Fact 1, and the stable sort keeps them in the order they had, which is input order by (G4). Pairs with one index outside keep their old relation, because the range holds the same strings as before.
- (G4) holds in each new task, because the stable sort keeps the order of equal strings.
Termination. Let . Each pushed task has depth and holds strings of length at least , so each task on the stack has depth at most . Give a task the weight , which is at least , and let be the total weight of the tasks on . When a task with strings is popped, its children have sizes that add up to at most , each with the factor , so drops by at least . When a task with at most one string is popped, does not grow. So at most pops handle a task with two or more strings. Each of them pushes at most tasks, so the number of pops is at most , and the loop ends.
Result. When is empty, (G3) holds for all pairs of indices. So is in lexicographic order, it holds the input strings, and equal strings are in input order.