Algorithm Search⌘K

Proofs: In-place linked list reversal

These are the human proofs of In-place linked list reversal, at full length.

REVERSE returns the same nodes in reverse order full

This proof shows: correctness, termination.

Claim. Let headhead be the first node of an acyclic singly linked list with nodes x1,…,xnx_1, \dots, x_n (n≥0n \ge 0), so that xi.next=xi+1x_i.next = x_{i+1} for 1≤i<n1 \le i < n and xn.next=nilx_n.next = \text{nil}, and head=nilhead = \text{nil} when n=0n = 0. Then REVERSE(head)(head) stops after exactly nn iterations of the loop of line L3. It returns xnx_n (nil when n=0n = 0), and after the call xi+1.next=xix_{i+1}.next = x_i for 1≤i<n1 \le i < n and x1.next=nilx_1.next = \text{nil}. No other field of any node changes, and no node is created.

Setup. Write x0=nilx_0 = \text{nil} and xn+1=nilx_{n+1} = \text{nil}. Count the iterations of the loop with kk, so that k=0k = 0 at the first test of line L3. The invariant at the test of line L3 after kk iterations is:

(R) 0≤k≤n0 \le k \le n, prev=xkprev = x_k, curr=xk+1curr = x_{k+1}, xi.next=xi−1x_i.next = x_{i-1} for 1≤i≤k1 \le i \le k, and xi.next=xi+1x_i.next = x_{i+1} for k<i≤nk < i \le n.

The third part says that the list from prevprev is xk→xk−1→⋯→x1→nilx_k \to x_{k-1} \to \dots \to x_1 \to \text{nil}. The last part says that the list from currcurr is xk+1→⋯→xn→nilx_{k+1} \to \dots \to x_n \to \text{nil}, with its old links.

Step 1 (initialization). Lines L1 and L2 set prev=nil=x0prev = \text{nil} = x_0 and curr=head=x1curr = head = x_1 (both nil when n=0n = 0). No link has changed, so xi.next=xi+1x_i.next = x_{i+1} for 0<i≤n0 < i \le n, and the condition for i≤0i \le 0 is empty. So (R) holds with k=0k = 0.

Step 2 (maintenance). Assume (R) for some kk, and that the test of line L3 succeeds, so curr≠nilcurr \ne \text{nil}. Since curr=xk+1curr = x_{k+1} and xk+1x_{k+1} is nil only for k=nk = n, we have k<nk < n, and currcurr is the real node xk+1x_{k+1}. By the last part of (R) with i=k+1i = k + 1, line L4 sets next=xk+1.next=xk+2next = x_{k+1}.next = x_{k+2}. Line L5 sets xk+1.next=prev=xkx_{k+1}.next = prev = x_k. This is the only write to a node field in the iteration, so the links of every other node stay as (R) states. Lines L6 and L7 set prev=xk+1prev = x_{k+1} and curr=xk+2curr = x_{k+2}. Now xi.next=xi−1x_i.next = x_{i-1} holds for 1≤i≤k+11 \le i \le k + 1, and xi.next=xi+1x_i.next = x_{i+1} holds for k+1<i≤nk + 1 < i \le n. So (R) holds for k+1k + 1, and k+1≤nk + 1 \le n.

Step 3 (termination). By (R), k≤nk \le n at each test of line L3, and each iteration increases kk by exactly 1. So the quantity n−kn - k is a non-negative integer that drops by exactly 1 in each iteration, and the loop runs at most nn times. The loop cannot stop while k<nk < n, because then curr=xk+1curr = x_{k+1} is a node, not nil. So it stops exactly when k=nk = n, after nn iterations.

Step 4 (conclusion). At exit, (R) holds with k=nk = n: prev=xnprev = x_n, and xi.next=xi−1x_i.next = x_{i-1} for 1≤i≤n1 \le i \le n, that is, xi+1.next=xix_{i+1}.next = x_i for 1≤i<n1 \le i < n and x1.next=x0=nilx_1.next = x_0 = \text{nil}. Line L8 returns prev=xnprev = x_n. The only writes to node fields are the nn writes of line L5, one to the next field of each node, and the algorithm creates no node. ■\blacksquare

Back to In-place linked list reversal