Insertion sort
Insertion sort takes elements one at a time and inserts each into its place within an already sorted prefix, the way you sort a hand of cards. It is stable, adaptive and the fastest elementary sort on small or nearly sorted input.
Learning goals
- Trace insertion sort and state its loop invariant
- Explain why shifting beats swapping
- Relate its running time to the number of inversions
- Use binary search to reduce comparisons
Prerequisites
Sorted prefix invariant
Invariant: at the start of iteration i, arr[0..i-1] is sorted (it holds the original first i elements in order). Inserting arr[i] into that prefix extends the invariant to arr[0..i].
When i reaches n, the whole array is sorted. This is a textbook example of proving correctness with a loop invariant: it holds at initialisation, each iteration maintains it, and it gives the result at termination.
Insertion sort
Interactive · edit the inputint[] arr · length 7
Invariant: arr[0..i-1] is always sorted. A single element is trivially sorted.
1public static void insertionSort(int[] arr) {2for (int i = 1; i < arr.length; i++) {3int key = arr[i];4int j = i - 1;5while (j >= 0 && arr[j] > key) {6arr[j + 1] = arr[j]; // shift right7j--;8}9arr[j + 1] = key;10}11}
Live state
n
7
Execution trace (dry run table)Show 48 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | n=7 | Invariant: arr[0..i-1] is always sorted. A single element is trivially sorted. |
| 2 | L3 | i=1 key=27 | Pick up key = 27. It must be inserted into the sorted prefix [0..0]. |
| 3 | L4 | i=1 key=27 j=0 | j = 0, the last index of the sorted prefix. |
| 4 | L5 | i=1 key=27 j=0 | 38 is larger, so it must move right. |
| 5 | L6 | i=1 key=27 j=0 | arr[1] = arr[0]: 38 shifts right. One write, not a full swap. |
| 6 | L5 | i=1 key=27 j=-1 | Reached the front of the array. |
| 7 | L9 | i=1 key=27 j=-1 | arr[0] = 27. Prefix [0..1] is sorted. |
| 8 | L3 | i=2 key=43 | Pick up key = 43. It must be inserted into the sorted prefix [0..1]. |
| 9 | L4 | i=2 key=43 j=1 | j = 1, the last index of the sorted prefix. |
| 10 | L5 | i=2 key=43 j=1 | 38 <= 43. Stop here; using > (not >=) keeps equal keys in order, so the sort is stable. |
| 11 | L9 | i=2 key=43 j=1 | arr[2] = 43. Prefix [0..2] is sorted. |
| 12 | L3 | i=3 key=3 | Pick up key = 3. It must be inserted into the sorted prefix [0..2]. |
| 13 | L4 | i=3 key=3 j=2 | j = 2, the last index of the sorted prefix. |
| 14 | L5 | i=3 key=3 j=2 | 43 is larger, so it must move right. |
| 15 | L6 | i=3 key=3 j=2 | arr[3] = arr[2]: 43 shifts right. One write, not a full swap. |
| 16 | L5 | i=3 key=3 j=1 | 38 is larger, so it must move right. |
| 17 | L6 | i=3 key=3 j=1 | arr[2] = arr[1]: 38 shifts right. One write, not a full swap. |
| 18 | L5 | i=3 key=3 j=0 | 27 is larger, so it must move right. |
| 19 | L6 | i=3 key=3 j=0 | arr[1] = arr[0]: 27 shifts right. One write, not a full swap. |
| 20 | L5 | i=3 key=3 j=-1 | Reached the front of the array. |
| 21 | L9 | i=3 key=3 j=-1 | arr[0] = 3. Prefix [0..3] is sorted. |
| 22 | L3 | i=4 key=9 | Pick up key = 9. It must be inserted into the sorted prefix [0..3]. |
| 23 | L4 | i=4 key=9 j=3 | j = 3, the last index of the sorted prefix. |
| 24 | L5 | i=4 key=9 j=3 | 43 is larger, so it must move right. |
| 25 | L6 | i=4 key=9 j=3 | arr[4] = arr[3]: 43 shifts right. One write, not a full swap. |
| 26 | L5 | i=4 key=9 j=2 | 38 is larger, so it must move right. |
| 27 | L6 | i=4 key=9 j=2 | arr[3] = arr[2]: 38 shifts right. One write, not a full swap. |
| 28 | L5 | i=4 key=9 j=1 | 27 is larger, so it must move right. |
| 29 | L6 | i=4 key=9 j=1 | arr[2] = arr[1]: 27 shifts right. One write, not a full swap. |
| 30 | L5 | i=4 key=9 j=0 | 3 <= 9. Stop here; using > (not >=) keeps equal keys in order, so the sort is stable. |
| 31 | L9 | i=4 key=9 j=0 | arr[1] = 9. Prefix [0..4] is sorted. |
| 32 | L3 | i=5 key=82 | Pick up key = 82. It must be inserted into the sorted prefix [0..4]. |
| 33 | L4 | i=5 key=82 j=4 | j = 4, the last index of the sorted prefix. |
| 34 | L5 | i=5 key=82 j=4 | 43 <= 82. Stop here; using > (not >=) keeps equal keys in order, so the sort is stable. |
| 35 | L9 | i=5 key=82 j=4 | arr[5] = 82. Prefix [0..5] is sorted. |
| 36 | L3 | i=6 key=10 | Pick up key = 10. It must be inserted into the sorted prefix [0..5]. |
| 37 | L4 | i=6 key=10 j=5 | j = 5, the last index of the sorted prefix. |
| 38 | L5 | i=6 key=10 j=5 | 82 is larger, so it must move right. |
| 39 | L6 | i=6 key=10 j=5 | arr[6] = arr[5]: 82 shifts right. One write, not a full swap. |
| 40 | L5 | i=6 key=10 j=4 | 43 is larger, so it must move right. |
| 41 | L6 | i=6 key=10 j=4 | arr[5] = arr[4]: 43 shifts right. One write, not a full swap. |
| 42 | L5 | i=6 key=10 j=3 | 38 is larger, so it must move right. |
| 43 | L6 | i=6 key=10 j=3 | arr[4] = arr[3]: 38 shifts right. One write, not a full swap. |
| 44 | L5 | i=6 key=10 j=2 | 27 is larger, so it must move right. |
| 45 | L6 | i=6 key=10 j=2 | arr[3] = arr[2]: 27 shifts right. One write, not a full swap. |
| 46 | L5 | i=6 key=10 j=1 | 9 <= 10. Stop here; using > (not >=) keeps equal keys in order, so the sort is stable. |
| 47 | L9 | i=6 key=10 j=1 | arr[2] = 10. Prefix [0..6] is sorted. |
| 48 | L11 | Sorted with 15 comparisons, 11 shifts. Shifts equal the number of inversions in the input. |
Shift vs swap
Hold the key in a variable and shift larger elements one slot right with a single write each, then drop the key into the gap. That is one write per moved element plus one for the key.
The swap version (gnome sort, in 17.05) does three writes per step. The comparisons are the same, but shifting moves less data.
1// swap version: three writes per step2for (int j = i; j > 0 && arr[j - 1] > arr[j]; j--) {3int tmp = arr[j]; arr[j] = arr[j - 1]; arr[j - 1] = tmp;4}56// shift version: one write per step7int key = arr[i], j = i - 1;8while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j--; }9arr[j + 1] = key;
Adaptive best case
Each shift fixes exactly one inversion, so the total work is O(n + I), where I is the number of inversions. Sorted input has I = 0 and needs n - 1 comparisons. Reverse sorted input has I = n(n-1)/2.
This is why TimSort and introsort hand small or nearly sorted runs to insertion sort.
Best
O(n)
Average
O(n²)
Worst
O(n²)
Space
O(1)
Stability
The loop condition arr[j] > key stops at an equal element, so the key is placed after existing equal keys and their order is preserved. Using >= would move the key past its equals and break stability.
Stability: what happens to equal keys?
InteractiveInput · letters show original order of equal keys
Insertion sort
Bubble sort
Selection sort
Shell sort
Binary insertion sort
Since the prefix is sorted, binary search can find the insert position in O(log i) comparisons. Use the upper-bound form (<=) so equal keys stay stable.
Comparisons fall to O(n log n) overall, but shifting is still O(n²) in the worst case. It helps when comparisons are expensive, for example comparing long strings or objects.
Best
O(n log n) comparisons
Worst
O(n²) moves
Space
O(1)
Binary insertion sort
Interactive · edit the inputint[] arr · length 7
Same idea as insertion sort, but find the insert position with binary search.
1public static void binaryInsertionSort(int[] arr) {2for (int i = 1; i < arr.length; i++) {3int key = arr[i];4int lo = 0, hi = i;5while (lo < hi) { // upper bound in arr[0..i)6int mid = (lo + hi) >>> 1;7if (arr[mid] <= key) lo = mid + 1;8else hi = mid;9}10System.arraycopy(arr, lo, arr, lo + 1, i - lo);11arr[lo] = key;12}13}
Live state
n
7
Execution trace (dry run table)Show 32 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | n=7 | Same idea as insertion sort, but find the insert position with binary search. |
| 2 | L4 | i=1 key=27 lo=0 hi=1 | key = 27. Search the sorted prefix [0..0]. |
| 3 | L7 | i=1 key=27 lo=0 hi=1 mid=0 | Insert at or before mid. |
| 4 | L9 | i=1 key=27 lo=0 | Insert position is 0. |
| 5 | L10 | i=1 key=27 lo=0 moved=1 | Shift 1 element right in one block copy and drop 27 at index 0. |
| 6 | L4 | i=2 key=43 lo=0 hi=2 | key = 43. Search the sorted prefix [0..1]. |
| 7 | L7 | i=2 key=43 lo=0 hi=2 mid=1 | Insert after mid (<= keeps equal keys stable). |
| 8 | L9 | i=2 key=43 lo=2 | Insert position is 2. |
| 9 | L10 | i=2 key=43 lo=2 moved=0 | Shift 0 elements right in one block copy and drop 43 at index 2. |
| 10 | L4 | i=3 key=3 lo=0 hi=3 | key = 3. Search the sorted prefix [0..2]. |
| 11 | L7 | i=3 key=3 lo=0 hi=3 mid=1 | Insert at or before mid. |
| 12 | L7 | i=3 key=3 lo=0 hi=1 mid=0 | Insert at or before mid. |
| 13 | L9 | i=3 key=3 lo=0 | Insert position is 0. |
| 14 | L10 | i=3 key=3 lo=0 moved=3 | Shift 3 elements right in one block copy and drop 3 at index 0. |
| 15 | L4 | i=4 key=9 lo=0 hi=4 | key = 9. Search the sorted prefix [0..3]. |
| 16 | L7 | i=4 key=9 lo=0 hi=4 mid=2 | Insert at or before mid. |
| 17 | L7 | i=4 key=9 lo=0 hi=2 mid=1 | Insert at or before mid. |
| 18 | L7 | i=4 key=9 lo=0 hi=1 mid=0 | Insert after mid (<= keeps equal keys stable). |
| 19 | L9 | i=4 key=9 lo=1 | Insert position is 1. |
| 20 | L10 | i=4 key=9 lo=1 moved=3 | Shift 3 elements right in one block copy and drop 9 at index 1. |
| 21 | L4 | i=5 key=82 lo=0 hi=5 | key = 82. Search the sorted prefix [0..4]. |
| 22 | L7 | i=5 key=82 lo=0 hi=5 mid=2 | Insert after mid (<= keeps equal keys stable). |
| 23 | L7 | i=5 key=82 lo=3 hi=5 mid=4 | Insert after mid (<= keeps equal keys stable). |
| 24 | L9 | i=5 key=82 lo=5 | Insert position is 5. |
| 25 | L10 | i=5 key=82 lo=5 moved=0 | Shift 0 elements right in one block copy and drop 82 at index 5. |
| 26 | L4 | i=6 key=10 lo=0 hi=6 | key = 10. Search the sorted prefix [0..5]. |
| 27 | L7 | i=6 key=10 lo=0 hi=6 mid=3 | Insert at or before mid. |
| 28 | L7 | i=6 key=10 lo=0 hi=3 mid=1 | Insert after mid (<= keeps equal keys stable). |
| 29 | L7 | i=6 key=10 lo=2 hi=3 mid=2 | Insert at or before mid. |
| 30 | L9 | i=6 key=10 lo=2 | Insert position is 2. |
| 31 | L10 | i=6 key=10 lo=2 moved=4 | Shift 4 elements right in one block copy and drop 10 at index 2. |
| 32 | L13 | Sorted with 12 comparisons, 11 shifts. Comparisons drop to O(n log n), but shifts are still O(n²). |
Practice ladder
Now try it yourself
Easy
- Insertion sort descending
- Insert one value into an already sorted array
Medium
- Insertion sort a linked list
- Sort an array where each element is at most k positions from its target in O(n k)
Advanced
- Count inversions and verify they equal insertion sort's shifts
- Library sort (gapped insertion sort) analysis
Key takeaways
- Insertion sort maintains a sorted prefix and inserts each new element by shifting.
- Work is O(n + inversions): linear on nearly sorted data, quadratic on reverse data.
- It is stable, in-place and the standard choice for small subarrays.