Algorithm Search⌘K

Proofs: Linked-list merge sort

These are the human proofs of Linked-list merge sort, at full length.

Bottom-up linked-list merge sort sorts the list stably and stops full

This proof shows: correctness, termination.

Let the input list have n≥1n \ge 1 nodes. Write x≺yx \prec y for "node xx came before node yy in the input". A sequence of nodes is stably sorted when its keys are non-decreasing and nodes with equal keys appear in the order ≺\prec.

Lemma 1 (one merge). Let lines L6 to L11 start with pp at the first node of a stably sorted run LL of a≥1a \ge 1 nodes, followed in the list by a stably sorted run RR of min⁡(w,r)\min(w, r) nodes, where rr is the number of nodes after LL, and let each node of LL come before each node of RR in ≺\prec. Then the loop of line L8 appends the nodes of LL and RR to tailtail, each exactly once, as a stably sorted sequence, and ends with qq at the first node after RR, or nil.

Proof. Line L6 walks aa nodes, so qq is the first node of RR, and a=min⁡(w,np)a = \min(w, n_p) where npn_p is the number of nodes from pp on. Each step of the loop of line L8 takes the first remaining node of LL (line L10) or of RR (line L9) and appends it (line L11). The counters aa and bb count the remaining nodes of LL and the nodes of RR that may still be taken, and q≠nilq \ne nil says that RR has nodes left when b>0b > 0. So the loop runs while either run has nodes left, and takes each node once. It takes from RR only when LL is empty or the head of RR has a strictly smaller key than the head of LL. By induction on the steps, the appended sequence is the stable merge: the node appended is a minimum of the remaining keys, because the heads of the two sorted runs are their minima; and among equal keys a node of LL is taken before a node of RR, which matches ≺\prec, while the order inside each run is kept. When both runs are used up, qq is the node after RR. □\square

Invariant (B). At the test of line L2 for width ww, the list from headhead holds all nn input nodes, linked in a line that ends with nil, and for each j≥0j \ge 0 the block of nodes at the positions jw,…,min⁡((j+1)w,n)−1jw, \ldots, \min((j+1)w, n) - 1 is stably sorted, and every node of block jj comes before every node of block j+1j+1 in ≺\prec.

Initialization. For w=1w = 1, each block has one node, and the list is the input list in input order.

One pass. The loop of line L4 starts with pp at block 0. By Lemma 1, the first merge appends blocks 0 and 1 as a stably sorted sequence, and leaves qq at the start of block 2, which line L12 copies to pp. The next merge handles blocks 2 and 3, and so on, until pp is nil. The nodes of different pairs are appended in the order of the pairs, and by (B) all nodes of pair tt come before all nodes of pair t+1t + 1 in ≺\prec. Line L13 ends the new list. So after the pass the list holds all nn nodes, and its blocks of 2w2w nodes are the merged pairs, each stably sorted, in the order ≺\prec between blocks. Line L15 sets ww to 2w2w, so (B) holds at the next test of line L2.

Termination. A pass with width ww makes ⌈n/w⌉/2\lceil n / w \rceil / 2 merges rounded up, that is ⌈n/2w⌉\lceil n / 2w \rceil, because each merge consumes two blocks, or one at the end. This is at most 1 exactly when n≤2wn \le 2w. Since ww doubles in each pass, a pass with 2w≥n2w \ge n comes after at most ⌈lg⁡n⌉\lceil \lg n \rceil passes, or 1 pass when n=1n = 1. Each inner loop stops: line L6 makes at most ww steps, the loop of line L8 appends one node per step and there are finitely many, and the loop of line L4 moves pp forward past at least one node per merge.

Correctness at the end. Line L14 returns after a pass with merges≤1merges \le 1. As n≥1n \ge 1, it made exactly one merge, of block 0 and block 1 of width ww, which together hold all nn nodes. By Lemma 1 the list from D.nextD.next is all nn nodes, stably sorted, and line L13 ended it with nil. ■\blacksquare

Back to Linked-list merge sort