Algorithm Search⌘K

Proofs: Linked list

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

The linked list operations keep the chain and return the right result full

This proof shows: correctness.

Setting. For a list LL, the chain of LL is the sequence of nodes v0=L.headv_0 = L.head, vk+1=vk.nextv_{k+1} = v_k.next, up to the first NIL.

Invariant (I). The chain of LL has exactly L.nL.n nodes v0,…,vn−1v_0, \ldots, v_{n-1}, they are distinct, and $v_{n-1}.next = $ NIL (or $L.head = $ NIL when n=0n = 0). The list held is (v0.key,…,vn−1.key)(v_0.key, \ldots, v_{n-1}.key).

We show that each operation, started in a state where (I) holds and its precondition holds, ends with (I) for the list that the list type asks for. Write ℓk=vk.key\ell_k = v_k.key for the old keys. A new node made in line L1 or L4 is not in the old chain, because it did not exist before.

PUSH-FRONT(L,x)(L, x). Line L1 makes a new node vv with key xx. Line L2 sets v.next=v0v.next = v_0 (or NIL if n=0n = 0). Line L3 sets L.head=vL.head = v and the count to n+1n + 1. The new chain is v,v0,…,vn−1v, v_0, \ldots, v_{n-1}: these are n+1n + 1 distinct nodes, because vv is new and the old nodes are distinct, and the old nextnext fields did not change, so the chain still ends at NIL after vn−1v_{n-1}. The list held is (x,ℓ0,…,ℓn−1)(x, \ell_0, \ldots, \ell_{n-1}).

INSERT-AFTER(L,p,x)(L, p, x), p=vjp = v_j for some 0≤j<n0 \le j < n. Line L4 makes a new node vv. Line L5 sets v.nextv.next to the old vj.nextv_j.next, which is vj+1v_{j+1}, or NIL when j=n−1j = n - 1. Line L6 then sets vj.next=vv_j.next = v. Only the nextnext field of vjv_j and of vv are written. So the new chain is v0,…,vj,v,vj+1,…,vn−1v_0, \ldots, v_j, v, v_{j+1}, \ldots, v_{n-1}, and it ends at NIL. These n+1n + 1 nodes are distinct, and the count is n+1n + 1. The list held is (ℓ0,…,ℓj,x,ℓj+1,…,ℓn−1)(\ell_0, \ldots, \ell_j, x, \ell_{j+1}, \ldots, \ell_{n-1}). Line L5 reads p.nextp.next before line L6 changes it; that order is what makes v.nextv.next the old successor.

DELETE-FRONT(L)(L), n≥1n \ge 1. Line L7 sets v=v0v = v_0. Line L8 sets L.head=v0.next=v1L.head = v_0.next = v_1 (or NIL if n=1n = 1) and the count to n−1n - 1. No nextnext field changes, so the new chain is v1,…,vn−1v_1, \ldots, v_{n-1}, with n−1n - 1 distinct nodes, ending at NIL. Line L9 returns ℓ0\ell_0, and the list held is (ℓ1,…,ℓn−1)(\ell_1, \ldots, \ell_{n-1}).

DELETE-AFTER(L,p)(L, p), p=vjp = v_j with j≤n−2j \le n - 2. The precondition p.next≠p.next \ne NIL means j≤n−2j \le n - 2. Line L10 sets v=vj+1v = v_{j+1}. Line L11 sets vj.next=vj+1.nextv_j.next = v_{j+1}.next, which is vj+2v_{j+2} or NIL, and the count to n−1n - 1. Only the nextnext field of vjv_j changes. The new chain is v0,…,vj,vj+2,…,vn−1v_0, \ldots, v_j, v_{j+2}, \ldots, v_{n-1}: n−1n - 1 distinct nodes ending at NIL. Line L12 returns ℓj+1\ell_{j+1}, and the list held is the old list without position j+1j + 1.

SEARCH(L,x)(L, x). Loop invariant: at the test of line S2, p=vkp = v_k for some 0≤k≤n0 \le k \le n (with vnv_n meaning NIL), and ℓt≠x\ell_t \ne x for every t<kt < k. It holds with k=0k = 0 after line S1. If the test passes, then p≠p \ne NIL and ℓk≠x\ell_k \ne x, and line S3 moves to vk+1v_{k+1}, so the invariant holds for k+1k + 1. The value kk grows by 1 in each iteration and cannot pass nn, because at k=nk = n the test fails on $p = $ NIL; by (I) the chain has nn nodes and no cycle, so the loop ends after at most nn iterations. At exit, either $p = $ NIL and k=nk = n, so no key equals xx and NIL is right, or p=vkp = v_k with ℓk=x\ell_k = x and no earlier key equal to xx, so pp is the first node with key xx.

NODE-AT(L,i)(L, i), 0≤i<n0 \le i < n. After line S5 p=v0p = v_0, and each of the ii steps of line S6 moves from vkv_k to vk+1v_{k+1} for k<i≤n−1k < i \le n - 1, so no step reads NIL. Line S7 returns viv_i. ■\blacksquare

Back to Linked list