Memra

Insertion sort & the loop-invariant method

◈ 6 cards

Prove insertion sort correct with the init/maintenance/termination template, and pin down its in-place, stable, best Θ(n) / worst Θ(n²) profile.

The incremental method

Insertion sort builds the answer one element at a time, exactly like sorting a hand of playing cards: keep the cards already in your hand sorted, pick up the next card, and slide it leftward past every larger card until it sits in its place. The already-sorted region grows by one each round until the whole hand is sorted.

CLRS writes the array 1-indexed. The outer loop runs from to ; key = A[i] is the card being inserted, and the inner while shifts each larger neighbour one slot right to open a gap for it:

INSERTION-SORT(A, n)
  for i = 2 to n
    key = A[i]
    j = i - 1
    while j > 0 and A[j] > key
      A[j + 1] = A[j]
      j = j - 1
    A[j + 1] = key

The loop invariant: proving it correct

A loop invariant is a property true before every iteration of a loop. Proving it is the algorithmic analogue of induction, and it is a near-guaranteed exam task. For insertion sort the invariant is:

> At the start of each iteration of the for loop, the subarray consists of the elements originally in , but in sorted order.

Notice the two clauses: same elements (nothing lost or invented) and sorted. Stating only "the left part is sorted" throws away the elements clause and the proof collapses. You discharge an invariant in three steps:

  1. Initialization (the base case). Before the first iteration , so is a single element — trivially sorted and trivially the original element. ✓
  2. Maintenance (the inductive step). Assume the invariant before iteration . The body moves one position right until it finds the slot where key belongs, then drops key in. So now holds the same elements, in sorted order — the invariant holds before iteration . ✓
  3. Termination (the payoff). The loop ends when . Substituting into the invariant gives: holds the original elements in sorted order — exactly the sorting postcondition. ✓

The three steps map onto base case / inductive step / conclusion. The conclusion step is the one that matters most: it is where you extract correctness by plugging the loop variable's final value into the invariant.

Properties

Insertion sort is in-place ( extra memory) and stable (equal keys keep their relative order, because the strict A[j] > key test never moves an equal element). Its running time depends on the input, not just its size:

  • Best case — input already sorted. The while test fails immediately every round, so total work is .
  • Worst case — input reverse-sorted. Element shifts past all predecessors, so the total shift count is .
  • Average case — each element lands about halfway into the sorted prefix, shifts: still .

That best case is why production sorts (Timsort) fall back to insertion sort on small or nearly-sorted runs.

123456i = 2524613isorteduntouchedi = 3254613isorteduntouchedi = 4245613isorteduntouchedi = 5245613isorteduntouchedi = 6124563isorteduntouchedi = 7123456sorted — doneEach row is the state at the top of an iteration. The loop exits at i = n + 1 = 7; substituting thatinto the invariant gives the sorting postcondition.
The loop invariant, pass by pass: everything left of i is sorted, and is exactly the elements that started there.
123456before245613ji, keysortedshifts24563j + 1each moved right 1after124563sortednextkey = A[5] = 1 is held aside while A[4], A[3], A[2], A[1] each move one slot right. The while test failsat j = 0, so key goes into A[j + 1] = A[1].
Maintenance, close up. Every element greater than key shifts right; key lands in the gap and the sorted region grows to A[1..5].
Initializationbase case — at i = 2, A[1..1] is trivially sortedMaintenanceinductive step — true before i ⟹ true before i + 1Terminationconclusion — exit at i = n + 1, so A[1..n] is sorted
The proof template. Termination is the step that actually pays out correctness, so never stop at maintenance.
NORMAL ~/memra/learn/comp-372/insertion-sort-loop-invariants utf-8 LF