Algorithm Search⌘K

Proofs: Unrolled linked list

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

The unrolled linked list keeps its invariant and implements the list type full

This proof shows: correctness.

Claim. Let b≥2b \ge 2 and h=⌊b/2⌋h = \lfloor b/2 \rfloor. If the invariant (U1) to (U3) holds before an operation, then GET(L,i)(L, i) for 0≤i<n0 \le i < n returns sis_i; INSERT(L,i,x)(L, i, x) for 0≤i≤n0 \le i \le n leaves the sequence (s0,…,si−1,x,si,…,sn−1)(s_0, \dots, s_{i-1}, x, s_i, \dots, s_{n-1}); DELETE(L,i)(L, i) for 0≤i<n0 \le i < n returns sis_i and leaves (s0,…,si−1,si+1,…,sn−1)(s_0, \dots, s_{i-1}, s_{i+1}, \dots, s_{n-1}); and the invariant holds afterwards.

Setup. The nodes are v1,…,vkv_1, \dots, v_k with counts c1,…,ckc_1, \dots, c_k, and Pt=c1+⋯+ct−1P_t = c_1 + \dots + c_{t-1} is the number of items before node vtv_t. By (U1), node vtv_t holds the positions PtP_t to Pt+ct−1P_t + c_t - 1 at its offsets 0 to ct−1c_t - 1. The invariant is:

(U1) the arrays of v1,…,vkv_1, \dots, v_k, read in order, form ss; (U2) 1≤ct≤b1 \le c_t \le b for each tt; (U3) if k≥2k \ge 2 then ct≥hc_t \ge h for each tt.

Since b≥2b \ge 2, h≥1h \ge 1, and b−h=⌈b/2⌉≥hb - h = \lceil b/2 \rceil \ge h.

Lemma 1 (FIND). Let L.head≠nilL.head \ne \text{nil} and 0≤i0≤n0 \le i_0 \le n. FIND(L,i0)(L, i_0) returns (p,vt,j)(p, v_t, j) with p=vt−1p = v_{t-1} (nil for t=1t = 1) and i0=Pt+ji_0 = P_t + j, where 0≤j<ct0 \le j < c_t if i0<ni_0 < n, and t=kt = k, j=ckj = c_k if i0=ni_0 = n.

Proof. The loop of lines L2 and L3 keeps: v=vtv = v_t, p=vt−1p = v_{t-1}, i=i0−Pti = i_0 - P_t, and i≥0i \ge 0. This holds at the start with t=1t = 1, P1=0P_1 = 0. A step runs only when i≥cti \ge c_t, and it sets i←i−ct=i0−Pt+1≥0i \leftarrow i - c_t = i_0 - P_{t+1} \ge 0 and moves to vt+1v_{t+1}, so the statement holds for t+1t + 1. Each step increases tt by 1 and the loop stops at the latest when t=kt = k, since then v.next=nilv.next = \text{nil}; so it stops after at most k−1k - 1 steps. At the exit, either i<cti < c_t, so j=ij = i satisfies 0≤j<ct0 \le j < c_t; or t=kt = k and i≥cki \ge c_k, so i0≥Pk+ck=ni_0 \ge P_k + c_k = n, hence i0=ni_0 = n and j=ckj = c_k. If i0<ni_0 < n, the second case is impossible, so 0≤j<ct0 \le j < c_t. If i0=ni_0 = n, the first case would give n=Pt+j<Pt+ct≤nn = P_t + j < P_t + c_t \le n, a contradiction, so t=kt = k and j=ckj = c_k. □\square

GET and SET. For 0≤i<n0 \le i < n, Lemma 1 gives the node vtv_t and an offset j<ctj < c_t with i=Pt+ji = P_t + j, which by (U1) is the cell of sis_i. Line L5 returns it. Neither changes the structure of the nodes, so the invariant holds.

Lemma 2 (INSERT). If the list is empty, line L6 makes one node holding xx, which satisfies (U1) to (U3) with k=1k = 1. Else let FIND return (p,vt,j)(p, v_t, j) with i=Pt+ji = P_t + j and 0≤j≤ct0 \le j \le c_t (by Lemma 1).

Case 1, ct<bc_t < b. Line L11 shifts the items at offsets jj to ct−1c_t - 1 up by one and writes xx at offset jj. Read in order, the arrays now give s0,…,sPt+j−1,x,sPt+j,…s_0, \dots, s_{P_t + j - 1}, x, s_{P_t + j}, \dots, which is the new sequence. The count becomes ct+1≤bc_t + 1 \le b, and no count drops, so (U2) and (U3) hold.

Case 2, ct=bc_t = b. Line L9 moves the last hh items of vtv_t to a new node ww placed after vtv_t, so vtv_t keeps b−hb - h items and the order of (U1) is unchanged. Both new counts b−hb - h and hh are at least h≥1h \ge 1. If j≤b−hj \le b - h, position ii is at offset jj of vtv_t, which now has b−hb - h items. If j>b−hj > b - h, position ii is at offset j−(b−h)j - (b - h) of ww, and 0<j−(b−h)≤h0 < j - (b - h) \le h; line L10 selects this node. Then line L11 inserts as in Case 1, and the chosen node gets b−h+1≤bb - h + 1 \le b or h+1≤bh + 1 \le b items (because h≥1h \ge 1 and b≥2b \ge 2). The nodes vtv_t and ww now hold between hh and bb items. Every other node keeps its old count, which is between hh and bb by (U2) and (U3), since the old list had vtv_t and at least one more node; when the old kk was 1 there is no other node. So (U2) and (U3) hold with k+1≥2k + 1 \ge 2 nodes. □\square

Lemma 3 (DELETE). Let FIND return (p,v,j)(p, v, j), with v=vtv = v_t, 0≤j<ct0 \le j < c_t, and i=Pt+ji = P_t + j. Line L13 returns sis_i by (U1) and shifts the later items of vv down, so the arrays read in order give the new sequence, and the count of vv is ct−1c_t - 1.

Case 1, ct−1≥hc_t - 1 \ge h, or k=1k = 1. If ct−1≥h≥1c_t - 1 \ge h \ge 1, all counts are still between hh and bb. If k=1k = 1 (that is, p=nilp = \text{nil} and v.next=nilv.next = \text{nil}) and ct−1≥1c_t - 1 \ge 1, (U3) does not apply. If k=1k = 1 and ct−1=0c_t - 1 = 0, line L14 removes the node and the list is empty with k=0k = 0. In each case the invariant holds.

Case 2, ct−1<hc_t - 1 < h and k≥2k \ge 2. Since ct≥hc_t \ge h by (U3), the count of vv is exactly h−1h - 1. Line L15 picks two adjacent nodes uu before rr, one of them vv: the successor of vv if it exists, else the predecessor pp, which exists since k≥2k \ge 2. Let oo be the other node of the pair; by (U3), oo has co≥hc_o \ge h items.

If (h−1)+co≤b(h - 1) + c_o \le b, line L16 appends the items of rr to uu. Since rr follows uu, the order of (U1) is unchanged. The merged node has h−1+co≥2h−1≥hh - 1 + c_o \ge 2h - 1 \ge h items (as h≥1h \ge 1) and at most bb. The list now has k−1k - 1 nodes; if k−1≥2k - 1 \ge 2 every count is still at least hh, and if k−1=1k - 1 = 1 (U3) does not apply. So the invariant holds.

If (h−1)+co>b(h - 1) + c_o > b, then co≥b−h+2≥h+1c_o \ge b - h + 2 \ge h + 1, because b+1≥2hb + 1 \ge 2h. Line L17 moves one item from oo to vv: the first item of rr to the end of uu when u=vu = v, or the last item of uu to the front of rr when r=vr = v. In both cases the moved item stays between the items of uu and the items of rr, so the order of (U1) is unchanged. Now vv has hh items and oo has co−1≥hc_o - 1 \ge h items, both at most bb, so (U2) and (U3) hold. □\square

Conclusion. The empty list (no node, n=0n = 0) satisfies the invariant. By Lemmas 2 and 3 and the paragraph on GET, every operation returns the value the list type asks for and keeps the invariant. By induction on the number of operations, any sequence of operations from the empty list behaves as the abstract list. ■\blacksquare

Back to Unrolled linked list