Back to blog
← View series: problem solving with dsa

~/blog

Insertion Sort

Jun 2, 20269 min readBy Mohammed Vasim
dsapythondata-structuresalgorithmsproblem-solving

Given an unsorted array, rearrange it in ascending order. Insertion sort builds the sorted portion one element at a time. It picks the first element from the unsorted portion, shifts larger elements in the sorted portion right by one, and inserts the picked element into the gap.

This is exactly how you sort a hand of playing cards: you pick up one card at a time and insert it where it belongs among the cards you already hold.


One Deep Walkthrough: [8, 5, 2, 9, 1]

The first element (8) is trivially sorted. We pick from index 1 onward.

Step 1 — pick key = arr[1] = 5

The sorted portion is [8]. Compare 5 with 8. Since 8 > 5, shift 8 right by one position.

Array stateAction
[8, 5, 2, 9, 1]key = 5. Compare 8 > 5 → shift 8 to index 1
[8, 8, 2, 9, 1]Sorted portion exhausted. Insert key at index 0
[5, 8, 2, 9, 1]Sorted: [5, 8]. Unsorted: [2, 9, 1]
Step 1 · key=5 · 8>5, shift 8 right → insert 5 at index 0 Before 8 5 key 2 9 1 shift 8 → After 5 8 2 9 1 5 inserted ✓

Step 2 — pick key = arr[2] = 2

Compare with 8 → shift. Compare with 5 → shift.

Step 2 — key = 2, shift larger elements right to open a gap Pick key 5 8 2 key 9 1 Shifts gap 5 8 9 1 key=2 ↓ Insert 2
Array stateAction
[5, 8, 2, 9, 1]key = 2. Compare 8 > 2 → shift 8 to index 2
[5, 8, 8, 9, 1]Compare 5 > 2 → shift 5 to index 1
[5, 5, 8, 9, 1]Sorted portion exhausted. Insert key at index 0
[2, 5, 8, 9, 1]Sorted: [2, 5, 8]. Unsorted: [9, 1]

Step 3 — pick key = arr[3] = 9

Compare with 8. 8 > 9? No. Stop immediately. Insert key at index 3 (its original position).

Array stateAction
[2, 5, 8, 9, 1]key = 9. Compare 8 > 9? No. Stop. Insert at index 3
[2, 5, 8, 9, 1]Sorted: [2, 5, 8, 9]. Unsorted: [1]
Step 3 · key=9 · 8 > 9? No · stop · insert at index 3 (no shift) Array 2 5 8 9 key 9 1 8 > 9? No → stop already in place ✓

Step 4 — pick key = arr[4] = 1

Compare with 9 → shift. Compare with 8 → shift. Compare with 5 → shift. Compare with 2 → shift.

Array stateAction
[2, 5, 8, 9, 1]key = 1. Compare 9 > 1 → shift 9 to index 4
[2, 5, 8, 9, 9]Compare 8 > 1 → shift 8 to index 3
[2, 5, 8, 8, 9]Compare 5 > 1 → shift 5 to index 2
[2, 5, 5, 8, 9]Compare 2 > 1 → shift 2 to index 1
[2, 2, 5, 8, 9]Sorted portion exhausted. Insert key at index 0
[1, 2, 5, 8, 9]Done
Step 4 · key=1 · shifts right through 9, 8, 5, 2 → inserts at index 0 Before 2 5 8 9 1 key Shift 9→ Shift 8→ Shift 5→ Shift 2→ Insert 1 1 ✓ sorted

Code

python
def insertion_sort(arr):
    n = len(arr)

    for i in range(1, n):
        key = arr[i]
        j = i - 1

        while j >= 0 and arr[j] > key:
            arr[j + 1] = arr[j]
            j -= 1

        arr[j + 1] = key

    return arr

Line by line:

  • for i in range(1, n): — start from index 1. Index 0 is the initial sorted portion.
  • key = arr[i] — save the element to be inserted. Without saving it, the shifts would overwrite it.
  • j = i - 1 — start comparing from the rightmost element of the sorted portion.
  • while j >= 0 and arr[j] > key: — shift condition. If the sorted element is greater than key, it must move right. The j >= 0 guard prevents accessing index -1.
  • arr[j + 1] = arr[j] — shift the element right by one position.
  • j -= 1 — move one position left to check the next sorted element.
  • arr[j + 1] = key — insert key at the gap. j + 1 is correct because j was decremented after the last shift (or never moved if no shift occurred).

Edge case — already sorted. The while condition arr[j] > key is always false. The inner loop never executes. arr[j + 1] = key writes key back to its own position (no-op). The array is scanned once. O(n).

Edge case — all identical. arr[j] > key is never true (equal is not greater). Zero shifts. O(n).

Time: O(n²) average and worst. O(n) best. Space: O(1).

Stability: Insertion sort is stable — equal elements keep their relative order because the shift stops at > (strictly greater), not >=.


Code Trace

Tracing insertion_sort([8, 5, 2, 9, 1]):

ikeyjwhile conditionActionArray state
1500 ≥ 0 and 8 > 5 → TrueShift 8 to index 1[8, 8, 2, 9, 1]
-1-1 ≥ 0 → FalseInsert key at j+1=0[5, 8, 2, 9, 1]
2211 ≥ 0 and 8 > 2 → TrueShift 8 to index 2[5, 8, 8, 9, 1]
00 ≥ 0 and 5 > 2 → TrueShift 5 to index 1[5, 5, 8, 9, 1]
-1-1 ≥ 0 → FalseInsert key at index 0[2, 5, 8, 9, 1]
3922 ≥ 0 and 8 > 9 → FalseExit while, insert at j+1=3[2, 5, 8, 9, 1]
4133 ≥ 0 and 9 > 1 → TrueShift 9 to index 4[2, 5, 8, 9, 9]
22 ≥ 0 and 8 > 1 → TrueShift 8 to index 3[2, 5, 8, 8, 9]
11 ≥ 0 and 5 > 1 → TrueShift 5 to index 2[2, 5, 5, 8, 9]
00 ≥ 0 and 2 > 1 → TrueShift 2 to index 1[2, 2, 5, 8, 9]
-1-1 ≥ 0 → FalseInsert key at index 0[1, 2, 5, 8, 9]

Insertion sort is the best O(n²) sort for nearly-sorted data. Python's Timsort uses it for small subarrays. It is adaptive (O(n) best case), stable, and has low overhead.

LeetCode 147: Insertion Sort List — the linked list version.

Comments (0)

No comments yet. Be the first to comment!

Leave a comment