Skip to main content

Insertion Sort

Overviewโ€‹

Insertion sort builds the sorted result one element at a time, taking the next element and sliding it back into its correct place among those already sorted โ€” exactly how most people sort a hand of playing cards.

It is O(nยฒ), and it is nonetheless the most used of the elementary sorts, because it is inside almost every production sorting routine. Below roughly 16โ€“32 elements it beats quicksort and mergesort outright, so those algorithms hand their small subarrays to it.

Animation of insertion sort: each new element is lifted out and moved leftward past larger elements until it reaches its position, with the sorted prefix growing one element at a time
The prefix on the left is always sorted. Each new element shifts left past everything larger than it, then drops into place. Wikimedia Commons, CC BY-SA 3.0

Core Conceptsโ€‹

PropertyValue
Best caseO(n) โ€” already sorted; one comparison per element, no shifts
AverageO(nยฒ)
Worst caseO(nยฒ) โ€” reverse sorted
SpaceO(1)
StableYes
AdaptiveYes, strongly โ€” O(n + d) where d is the number of inversions
OnlineYes โ€” can sort a stream as elements arrive

Architecture / Mechanismโ€‹

def insertion_sort(a):
for i in range(1, len(a)):
key = a[i]
j = i - 1
# Shift everything greater than key one position right
while j >= 0 and a[j] > key:
a[j + 1] = a[j]
j -= 1
a[j + 1] = key # drop key into the gap
return a

Note that the inner loop shifts rather than swaps โ€” one write per displaced element instead of three. That is roughly a 3ร— constant-factor win over the swap-based formulation, and it is why insertion sort outperforms bubble sort on the same asymptotics.

Tracing [5, 1, 4, 2]:

StepKeyActionResult
i=11shift 5 right, insert 1[1, 5, 4, 2]
i=24shift 5 right, insert 4[1, 4, 5, 2]
i=32shift 5 and 4 right, insert 2[1, 2, 4, 5]

Why "adaptive" is the important wordโ€‹

The inner loop runs only while elements are out of order, so the total work is proportional to the number of inversions โ€” pairs that are in the wrong relative order. Formally the cost is O(n + d), and for nearly-sorted data d is small:

InputInversionsCost
Already sorted0O(n)
One element out of placeO(n)O(n)
Every element within k positions of its homeO(nk)O(nk)
Reverse sortedn(nโˆ’1)/2O(nยฒ)

Real data is very often nearly sorted โ€” appended log lines, mostly-ordered records, a sorted list with a few recent additions. This property is what Timsort is built to exploit.

Practical Usageโ€‹

# The way insertion sort is actually used: as the base case of a bigger sort
SMALL = 16

def hybrid_sort(a, lo, hi):
if hi - lo < SMALL:
insertion_sort_range(a, lo, hi) # cheap, cache-friendly, no recursion
return
p = partition(a, lo, hi)
hybrid_sort(a, lo, p)
hybrid_sort(a, p + 1, hi)

The reason this wins below the threshold: insertion sort has almost no per-element overhead, does no recursion, allocates nothing, and touches memory strictly sequentially. Quicksort's partitioning and recursion cost more than the quadratic term saves at those sizes.

Binary insertion sort โ€” using binary search to find the insertion point โ€” reduces comparisons to O(n log n) but leaves the shifting at O(nยฒ). It helps only when comparisons are much more expensive than moves.

Edge Cases & Pitfallsโ€‹

  • Swapping instead of shifting triples the writes for no benefit. Write the shift form.
  • The j >= 0 bound must come first in the while condition; reversing the operands indexes a[-1] in Python (silently wrapping to the end) rather than failing.
  • Use > not >= in the comparison. >= shifts past equal elements and destroys stability.
  • It is still O(nยฒ). The adaptivity is real, but on genuinely random input of any size it loses badly โ€” this is a small-input and nearly-sorted-input tool.

Comparisonsโ€‹

InsertionBubbleSelection
Best caseO(n)O(n)O(nยฒ)
Writes on random input~nยฒ/4 shifts~nยฒ/2 swaps (ร—3 writes)n โˆ’ 1 swaps
StableYesYesNo
AdaptiveStronglyWeaklyNo
OnlineYesNoNo
Used in practiceYes โ€” inside Timsort, introsort, pdqsortNoRarely

Referencesโ€‹

  • Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms, ยง2.1 โ€” insertion sort is the book's first algorithm, with its loop invariant proved in full.
  • Sedgewick & Wayne, Algorithms, 4th ed., ยง2.1 โ€” the inversion-count analysis behind the adaptivity claim.

Books & Videosโ€‹

  • Choosing a Sort โ€” Timsort and introsort, where this algorithm actually lives.
  • Quicksort โ€” the sort that delegates its small subarrays here.