/

Insertion Sort

Insertion sort is a simple, intuitive sorting algorithm that builds the final sorted array one element at a time. It works much the way you might sort a hand of playing cards: pick up each card and slide it into the correct position among the cards you have already sorted. It is efficient for small datasets and nearly-sorted arrays, and its adaptive nature makes it a practical choice in many real-world scenarios.

Sorting O(n²) Stable Adaptive

How Insertion Sort Works

Insertion sort iterates through the array starting from the second element. For each element (called the "key"), it compares the key with elements in the already-sorted portion to its left and shifts larger elements one position to the right to make room. The key is then inserted into its correct position.

Step-by-step process:

  • Start with the second element: The first element is trivially sorted. Pick the second element as the first key.
  • Extract the key: Remove the current element (the key) from its position. This creates a gap in the array.
  • Compare and shift: Moving right to left through the sorted portion, compare each element with the key. If the element is greater than the key, shift it one position to the right.
  • Insert the key: Once you find an element smaller than or equal to the key (or reach the beginning of the array), insert the key into the gap.
  • Repeat: Move to the next unsorted element and repeat the process until the entire array is sorted.
  • Adaptive behavior: If the array is already nearly sorted, very few shifts are needed on each pass. In the best case (already sorted), insertion sort only makes n-1 comparisons and zero shifts, achieving O(n) time complexity.

Insertion sort is stable (equal elements retain their original relative order), in-place (uses O(1) extra memory), and adaptive (performs better when the input is partially sorted). These properties make it an excellent choice for small arrays, and it is often used as the base case in hybrid sorting algorithms like Timsort.

Interactive Visualization

Watch how insertion sort picks each key element and inserts it into the sorted portion. The purple bar is the key being inserted, yellow bars indicate elements shifting right, and green bars are in the sorted portion.

Press Play or Step to start the visualization.
Speed No steps

Code Implementation

Below is an insertion sort implementation in three languages. The algorithm picks each element from the unsorted portion and shifts larger elements to the right until the correct insertion position is found.

def insertion_sort(arr):
    """Sort array using insertion sort."""
    n = len(arr)
    for i in range(1, n):
        key = arr[i]
        # Shift elements greater than key to the right
        j = i - 1
        while j >= 0 and arr[j] > key:
            arr[j + 1] = arr[j]
            j -= 1
        # Insert the key at its correct position
        arr[j + 1] = key
    return arr


# Example usage
numbers = [64, 34, 25, 12, 22, 11, 90, 45]
print("Unsorted:", numbers)
print("Sorted:  ", insertion_sort(numbers))
public class InsertionSort {

    public static void insertionSort(int[] arr) {
        int n = arr.length;
        for (int i = 1; i < n; i++) {
            int key = arr[i];
            // Shift elements greater than key to the right
            int j = i - 1;
            while (j >= 0 && arr[j] > key) {
                arr[j + 1] = arr[j];
                j--;
            }
            // Insert the key at its correct position
            arr[j + 1] = key;
        }
    }

    public static void main(String[] args) {
        int[] numbers = {64, 34, 25, 12, 22, 11, 90, 45};
        System.out.print("Unsorted: ");
        printArray(numbers);

        insertionSort(numbers);

        System.out.print("Sorted:   ");
        printArray(numbers);
    }

    private static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]);
            if (i < arr.length - 1) System.out.print(", ");
        }
        System.out.println();
    }
}
#include <iostream>
#include <vector>
using namespace std;

void insertionSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        // Shift elements greater than key to the right
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        // Insert the key at its correct position
        arr[j + 1] = key;
    }
}

void printArray(const vector<int>& arr) {
    for (int i = 0; i < arr.size(); i++) {
        cout << arr[i];
        if (i < arr.size() - 1) cout << ", ";
    }
    cout << endl;
}

int main() {
    vector<int> numbers = {64, 34, 25, 12, 22, 11, 90, 45};
    cout << "Unsorted: ";
    printArray(numbers);

    insertionSort(numbers);

    cout << "Sorted:   ";
    printArray(numbers);
    return 0;
}

Complexity Analysis

Insertion sort's performance varies significantly based on the initial order of elements. Its adaptive nature means it excels on nearly-sorted data, achieving linear time in the best case. For random or reverse-sorted data, it degrades to quadratic time due to the number of shifts required.

Metric Complexity Notes
Best Case Time O(n) Array is already sorted; only n-1 comparisons, no shifts
Average Case Time O(n²) Elements are in random order; approximately n²/4 comparisons and shifts
Worst Case Time O(n²) Array is sorted in reverse order; maximum shifts on every pass
Space Complexity O(1) In-place sorting; only uses a single temporary variable for the key
Stable Yes Equal elements maintain their relative order (shifts only on strict inequality)

Quiz

Test your understanding of insertion sort with these questions.

Q1. What is the best-case time complexity of insertion sort?

O(n²)
O(n log n)
O(n)
O(log n)
When the array is already sorted, insertion sort only performs n-1 comparisons and no shifts. Each key is already in its correct position, so the inner while loop never executes, giving O(n) time.

Q2. During insertion sort, what happens to elements that are greater than the key?

They are swapped with the key directly
They are shifted one position to the right
They are moved to the end of the array
They remain in their current position
Insertion sort shifts elements greater than the key one position to the right to make room for the key to be inserted at its correct position. This is different from bubble sort, which swaps adjacent elements.

Q3. After 3 passes of insertion sort on the array [50, 30, 40, 10, 20], what is the state of the array?

[10, 30, 40, 50, 20]
[10, 20, 30, 40, 50]
[30, 40, 50, 10, 20]
[30, 40, 10, 20, 50]
Pass 1 (key=30): 30 < 50, shift 50 right, insert 30 at index 0 -> [30, 50, 40, 10, 20]. Pass 2 (key=40): 40 < 50, shift 50 right, 40 >= 30, insert at index 1 -> [30, 40, 50, 10, 20]. Pass 3 (key=10): 10 < 50, shift 50; 10 < 40, shift 40; 10 < 30, shift 30; insert at index 0 -> [10, 30, 40, 50, 20].

Q4. Why is insertion sort considered "adaptive"?

It adapts by choosing different pivot elements
It changes its algorithm based on the data type
It uses different amounts of memory depending on the input
Its running time decreases when the input is partially sorted
An adaptive sorting algorithm performs better when the input has existing order. Insertion sort is adaptive because the number of shifts per element depends on how far it is from its sorted position. A nearly-sorted array requires very few shifts, approaching O(n) time.

Q5. Which of the following is a key advantage of insertion sort over merge sort?

Better worst-case time complexity
O(1) space complexity (in-place)
Faster on large random datasets
It is not stable
Insertion sort is an in-place algorithm that uses only O(1) extra space, whereas merge sort requires O(n) extra space for the merging step. Both are stable, but merge sort has better worst-case time. Insertion sort is also faster on small or nearly-sorted arrays due to lower overhead.