Insertion Sort
Insertion Sort inserts each element into the already-sorted prefix, repeating until all elements are ordered.
The mental model is the one you use when you sort a hand of playing cards: you hold a sorted group in one hand and pick up one card at a time from the other, finding where it belongs in the group and putting it there. The sorted prefix grows by one element per pass, and the invariant — “everything before index is sorted” — holds at the top of every pass.
Click "Step forward" to watch the algorithm state change.
- From the 2nd element onward, compare backward with the sorted part and insert at the right spot.
- Repeat until all elements are placed.
Pseudo-code:
for i = 1 to n-1:
key = A[i]
j = i-1
while j >= 0 and A[j] > key:
A[j+1] = A[j]
j--
A[j+1] = key
Notice that the element is not “put in place” with a single assignment. It is carried to the left while larger elements slide right, and dropped into the gap that opens up. That is why the cost of one pass is proportional to how far the element has to travel, not to where it started.
void InsertSort(int A[], int n) {
int i, j, key;
for (i = 1; i < n; i++) {
key = A[i]; // the element to insert
j = i - 1;
while (j >= 0 && A[j] > key) {
A[j + 1] = A[j]; // shift the larger element right
j--;
}
A[j + 1] = key; // drop it into the gap
}
}
Two details in this short routine are worth naming. The comparison uses > and not >=: with >=, elements that compare equal would be swapped and the sort would no longer be stable. And key is saved before the shifting loop, because the shifting overwrites A[i] — the element would be destroyed on the first iteration without a temporary copy.
Binary insertion sort
The comparisons of direct insertion sort can be optimised with a binary search: the prefix is already sorted, so the insertion position can be located by binary search rather than by walking backward. The number of comparisons drops from per pass to , while the number of moves stays the same — the elements still have to slide.
void BinaryInsertSort(int A[], int n) {
int i, j, low, high, mid, key;
for (i = 1; i < n; i++) {
key = A[i];
low = 0; high = i - 1;
while (low <= high) { // binary search for the insertion point
mid = (low + high) / 2;
if (A[mid] > key) high = mid - 1;
else low = mid + 1;
}
for (j = i - 1; j >= low; j--) // shift the tail of the prefix right
A[j + 1] = A[j];
A[low] = key;
}
}
The gain is real but bounded, and it is worth knowing why. Comparisons happen in registers and are cheap; moves happen in memory and are not. Since the moves are unchanged, binary insertion sort improves the constant factor of the comparison-heavy part without changing the overall order of growth. It also has to be careful about which of several equal elements it picks, or stability is lost — the A[mid] > key test above places the new element after equal ones, which is the stable choice.
- Best: (already sorted)
- Worst/avg:
- Space:
- Stability: stable
The best case is the interesting one. If the input is already sorted, the inner while loop never executes, and each pass costs a single comparison — overall. This is a property insertion sort shares with merge sort and quick sort, and it is the reason insertion sort is not a bad choice for nearly sorted data.
Exercise 1
Insert-sort and show each pass.
Approach: Each pass inserts one element into the sorted prefix in front of it.
Steps:
- Initial: [5, 2, 4, 6, 1, 3]
- Pass 1: [2, 5, 4, 6, 1, 3]
- Pass 2: [2, 4, 5, 6, 1, 3]
- Pass 3: [2, 4, 5, 6, 1, 3]
- Pass 4: [1, 2, 4, 5, 6, 3]
- Pass 5: [1, 2, 3, 4, 5, 6]
Answer: As shown above; the array becomes sorted step by step.
Exercise 2
What are the time and space complexities of insertion sort?
Approach: Consider the time complexity and the extra space.
Steps:
- Time complexity
- Space complexity
Answer: Time , space .
