G27
Self-checkedTier 1 14 min readalgo.sorting-elementary

Bubble, selection and insertion sort

The three elementary quadratic sorts: how each works and its invariant, exact comparison and swap counts on best and worst inputs, inversions as the common measure (bubble swaps and insertion shifts both equal the number of inversions), adaptivity, binary insertion sort, and why adjacent-swap sorts need quadratic time on average.

Why it exists

The first sorting algorithms anyone writes are short, in place and easy to get right, and they run in time proportional to n2n^2. They are not used for large inputs, but they matter for three reasons: GATE asks about their exact counts (comparisons, swaps) on particular inputs; one of them, insertion sort, is the fastest simple method for small or nearly sorted inputs and is the finishing step inside the fast sorts; and the idea of inversions that explains them also explains why no algorithm that moves one neighbour at a time can be fast.

Prerequisites

You need to count loop iterations and nested loops (analysing loops).

Precise definitions

Sorting. Rearrange a0,…,an−1a_0, \ldots, a_{n-1} so that a0≤a1≤⋯≤an−1a_0 \le a_1 \le \cdots \le a_{n-1}. The output must be a permutation of the input.

Comparison sort. An algorithm that learns about the order of the keys only by comparing two of them. All sorts on this page are comparison sorts.

In place. Uses only O(1)O(1) extra memory besides the array.

Stable. Keys that are equal appear in the output in the same order as in the input (stability and comparison of sorts).

Inversion. A pair of positions i<ji < j with ai>aja_i > a_j. A sorted array has 00 inversions, a strictly decreasing array has n(n−1)/2n(n-1)/2. The number of inversions measures how far an array is from sorted. Averaged over all n!n! orderings of distinct keys it is n(n−1)/4n(n-1)/4.

Adaptive. Runs faster on inputs that are already nearly sorted.

How it works

Bubble sort. Repeatedly walk through the array comparing neighbours and swapping those that are out of order. After pass pp the largest pp keys are in their final places at the right end, so each pass can stop one position earlier. If a pass makes no swap the array is sorted and the algorithm can stop (the early exit).

void bubble_sort(int a[], int n) {
    for (int pass = 0; pass < n - 1; pass++) {
        int swapped = 0;
        for (int j = 0; j < n - 1 - pass; j++) {
            if (a[j] > a[j + 1]) {
                int t = a[j]; a[j] = a[j + 1]; a[j + 1] = t;
                swapped = 1;
            }
        }
        if (!swapped) break;
    }
}

Bubble sort with the early exit

Real run

C source

1#include <stdio.h>
2
3void bubble_sort(int a[], int n) {
4 for (int pass = 0; pass < n - 1; pass++) {
5 int swapped = 0;
6 for (int j = 0; j < n - 1 - pass; j++) {
7 if (a[j] > a[j + 1]) {
8 int t = a[j];
9 a[j] = a[j + 1];
10 a[j + 1] = t;
11 swapped = 1;
12 }
13 }
14 if (!swapped) {
15 break;
16 }
17 }
18}

Array a (green = final position)

5
0
1
1
4
2
2
3
8
4
7
5
3
6

Counts

pass = 0, comparisons = 0, swaps = 0

Blue = the position this step is working on, amber = a position being compared, red = positions just swapped, green = finished or in its final place, a filled blue cell = a value just placed, faded = not part of the structure at this step.

Step 1

Sort 5, 1, 4, 2, 8, 7, 3 with bubble sort.

1 / 42
The array 5, 1, 4, 2, 8, 7, 3 is set up by hidden driver code. bubble_sort() is the exact code that runs. Green cells are in their final positions. Produced by: real execution: every array, stack, matrix and list in the picture is read from the running program's memory at each step. Apple clang version 21.0.0 (clang-2100.1.1.101). gcc -O0 -o prog prog.c && ./prog

Selection sort. Find the smallest key of the unsorted part and swap it into the first unsorted position. After pass ii the i+1i + 1 smallest keys are in their final places.

void selection_sort(int a[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int min = i;
        for (int j = i + 1; j < n; j++)
            if (a[j] < a[min]) min = j;
        if (min != i) { int t = a[i]; a[i] = a[min]; a[min] = t; }
    }
}

Selection sort: find the minimum of the rest, swap it into place

Real run

C source

1#include <stdio.h>
2
3void selection_sort(int a[], int n) {
4 for (int i = 0; i < n - 1; i++) {
5 int min = i;
6 for (int j = i + 1; j < n; j++) {
7 if (a[j] < a[min]) {
8 min = j;
9 }
10 }
11 if (min != i) {
12 int t = a[i];
13 a[i] = a[min];
14 a[min] = t;
15 }
16 }
17}

Array a (green = final position)

5
0
1
1
4
2
2
3
8
4
7
5
3
6

Counts

pass = 0, comparisons = 0, swaps = 0

Blue = the position this step is working on, amber = a position being compared, red = positions just swapped, green = finished or in its final place, a filled blue cell = a value just placed, faded = not part of the structure at this step.

Step 1

Sort 5, 1, 4, 2, 8, 7, 3 with selection sort.

1 / 47
The array 5, 1, 4, 2, 8, 7, 3 is set up by hidden driver code. selection_sort() is the exact code that runs. Green cells are in their final positions. Produced by: real execution: every array, stack, matrix and list in the picture is read from the running program's memory at each step. Apple clang version 21.0.0 (clang-2100.1.1.101). gcc -O0 -o prog prog.c && ./prog

Insertion sort. Keep a sorted prefix. Take the next key, shift the larger keys of the prefix one place to the right, and drop the key into the gap. The prefix grows by one; the keys of the prefix are not yet in their final places (a later small key will move them).

void insertion_sort(int a[], int n) {
    for (int i = 1; i < n; i++) {
        int key = a[i], j = i - 1;
        while (j >= 0 && a[j] > key) {
            a[j + 1] = a[j];
            j--;
        }
        a[j + 1] = key;
    }
}

Insertion sort: shift larger keys right, drop the key into the gap

Real run

C source

1#include <stdio.h>
2
3void insertion_sort(int a[], int n) {
4 for (int i = 1; i < n; i++) {
5 int key = a[i];
6 int j = i - 1;
7 while (j >= 0 && a[j] > key) {
8 a[j + 1] = a[j];
9 j--;
10 }
11 a[j + 1] = key;
12 }
13}

Array a (green = sorted prefix cells not being touched)

5
0
1
1
4
2
2
3
8
4
7
5
3
6

Counts

key in hand = 0, comparisons = 0, key moves and writes = 0

Blue = the position this step is working on, amber = a position being compared, red = positions just swapped, green = finished or in its final place, a filled blue cell = a value just placed, faded = not part of the structure at this step.

Step 1

Sort 5, 1, 4, 2, 8, 7, 3 with insertion sort. The prefix of length 1 is trivially sorted.

1 / 38
The array 5, 1, 4, 2, 8, 7, 3 is set up by hidden driver code. insertion_sort() is the exact code that runs. Green cells are the part of the sorted prefix that is not being touched (they are not necessarily final positions). Produced by: real execution: every array, stack, matrix and list in the picture is read from the running program's memory at each step. Apple clang version 21.0.0 (clang-2100.1.1.101). gcc -O0 -o prog prog.c && ./prog

Measured counts. The C program below runs all three on five inputs of 88 keys and counts comparisons of keys and moves (swaps for bubble and selection sort, shifts for insertion sort). Its code is the same as above with counters added, and it was compiled and run to produce the output.

#include <stdio.h>
 
typedef struct { int cmp, mv; } Count;   /* comparisons of keys; swaps (bubble, selection) or shifts (insertion) */
 
static Count bubble(int a[], int n) {
    Count c = {0, 0};
    for (int pass = 0; pass < n - 1; pass++) {
        int swapped = 0;
        for (int j = 0; j < n - 1 - pass; j++) {
            c.cmp++;
            if (a[j] > a[j + 1]) {
                int t = a[j]; a[j] = a[j + 1]; a[j + 1] = t;
                c.mv++;
                swapped = 1;
            }
        }
        if (!swapped) break;
    }
    return c;
}
 
static Count selection(int a[], int n) {
    Count c = {0, 0};
    for (int i = 0; i < n - 1; i++) {
        int min = i;
        for (int j = i + 1; j < n; j++) {
            c.cmp++;
            if (a[j] < a[min]) min = j;
        }
        if (min != i) {
            int t = a[i]; a[i] = a[min]; a[min] = t;
            c.mv++;
        }
    }
    return c;
}
 
static Count insertion(int a[], int n) {
    Count c = {0, 0};
    for (int i = 1; i < n; i++) {
        int key = a[i], j = i - 1;
        while (j >= 0 && (c.cmp++, a[j] > key)) {   /* the comma counts every comparison of keys */
            a[j + 1] = a[j];
            c.mv++;
            j--;
        }
        a[j + 1] = key;
    }
    return c;
}
 
static int inversions(const int a[], int n) {
    int inv = 0;
    for (int i = 0; i < n; i++)
        for (int j = i + 1; j < n; j++)
            if (a[i] > a[j]) inv++;
    return inv;
}
 
int main(void) {
    const char *name[5] = {"sorted", "reversed", "one swap", "mixed", "all equal"};
    int in[5][8] = {{1, 2, 3, 4, 5, 6, 7, 8}, {8, 7, 6, 5, 4, 3, 2, 1}, {1, 2, 4, 3, 5, 6, 7, 8}, {5, 1, 4, 2, 8, 7, 3, 6}, {4, 4, 4, 4, 4, 4, 4, 4}};
    printf("%-10s %3s | %-9s | %-9s | %-9s\n", "input", "inv", "bubble", "selection", "insertion");
    for (int r = 0; r < 5; r++) {
        int x[8], y[8], z[8];
        for (int i = 0; i < 8; i++) x[i] = y[i] = z[i] = in[r][i];
        int inv = inversions(in[r], 8);
        Count b = bubble(x, 8), s = selection(y, 8), t = insertion(z, 8);
        printf("%-10s %3d | %3d %3d   | %3d %3d   | %3d %3d\n", name[r], inv, b.cmp, b.mv, s.cmp, s.mv, t.cmp, t.mv);
    }
    return 0;
}
input      inv | bubble    | selection | insertion
sorted       0 |   7   0   |  28   0   |   7   0
reversed    28 |  28  28   |  28   4   |  28  28
one swap     1 |  13   1   |  28   1   |   8   1
mixed       11 |  25  11   |  28   7   |  17  11
all equal    0 |   7   0   |  28   0   |   7   0

Elementary sorts on 8 keys, measured: start

Computed by a script, not typed
inputinversionsbubble: comparisonsbubble: swapsselection: comparisonsselection: swapsinsertion: comparisonsinsertion: shifts
sorted
reversed
one swap
mixed
all equal

Step 0

Bubble sort (with the early exit), selection sort and insertion sort were run on five inputs of 8 keys, counting comparisons of keys and swaps (shifts for insertion sort). The numbers come from the C program on this page.

0 / 5
computed by script: the quantities are measured by running C, or evaluated, one row at a time. python3 scripts/gen-algo-sort-traces.py

What the numbers say, and why.

  • Inversions are the common currency. Bubble sort swaps adjacent keys that are in the wrong order, and every such swap removes exactly one inversion, so it makes exactly II swaps, where II is the number of inversions. Insertion sort shifts a key past exactly the larger keys before it, which are exactly the inversions that key forms, so it makes exactly II shifts. In the table the swaps of bubble sort and the shifts of insertion sort equal the inversion column in every row.
  • Selection sort ignores the input. Finding the minimum of n−in - i keys takes n−i−1n - i - 1 comparisons however they are ordered, so the total is always n(n−1)/2n(n-1)/2 (2828 for n=8n = 8 in every row). It makes at most n−1n - 1 swaps, the fewest of the three, which matters when moving a record is expensive.
  • Bubble sort with the early exit makes n−1n - 1 comparisons on a sorted array (one pass) and n(n−1)/2n(n-1)/2 on a reversed one. The number of passes is 1+1 + the largest number of larger keys to the left of any key (at most n−1n - 1), because each pass moves a key left by at most one place.
  • Insertion sort makes one failed comparison per key unless the key goes to the very front, so its comparisons are I+(n−1)−fI + (n - 1) - f, where ff is the number of keys that are smaller than everything before them: between n−1n - 1 (sorted) and n(n−1)/2n(n-1)/2 (reversed). It is adaptive: its time is Θ(n+I)\Theta(n + I).
  • Worst case for all three is Θ(n2)\Theta(n^2); best case is Θ(n)\Theta(n) for bubble sort with the early exit and for insertion sort, and Θ(n2)\Theta(n^2) for selection sort. Average case over random permutations of distinct keys: I=n(n−1)/4I = n(n-1)/4 swaps or shifts, so Θ(n2)\Theta(n^2) for all three.

Binary insertion sort. The place for the key inside the sorted prefix can be found with binary search (binary search), cutting the comparisons to ∑⌈log⁡2(i+1)⌉=O(nlog⁡n)\sum \lceil \log_2 (i + 1) \rceil = O(n \log n). But the shifts remain II, so the time is still Θ(n2)\Theta(n^2) in the worst case.

Why adjacent swaps cannot be fast. One swap of two adjacent keys changes the number of inversions by at most one (by exactly one when the two keys differ). An array with II inversions therefore needs at least II such swaps. Since a random array has n(n−1)/4n(n-1)/4 inversions on average, any sort that moves keys only by adjacent swaps takes Ω(n2)\Omega(n^2) time on average. The faster sorts (merge, quick, heap) win by moving a key over a long distance in one step, which removes many inversions at once.

Worked examples

EX 1One pass of bubble sort

Easy

Start with 5,1,4,2,8,7,35, 1, 4, 2, 8, 7, 3. Compare neighbours left to right: (5,1)(5,1) swap →1,5,4,2,8,7,3\to 1,5,4,2,8,7,3; (5,4)(5,4) swap →1,4,5,2,8,7,3\to 1,4,5,2,8,7,3; (5,2)(5,2) swap →1,4,2,5,8,7,3\to 1,4,2,5,8,7,3; (5,8)(5,8) no swap; (8,7)(8,7) swap →1,4,2,5,7,8,3\to 1,4,2,5,7,8,3; (8,3)(8,3) swap →1,4,2,5,7,3,8\to 1,4,2,5,7,3,8. That was 66 comparisons and 55 swaps; the largest key, 88, is at its final place. The animation shows the same pass.

EX 2Inversions equal the swaps

Easy

For 5,1,4,2,8,7,35, 1, 4, 2, 8, 7, 3 the inversions are: 55 forms four (1,4,2,31, 4, 2, 3), 11 none, 44 forms two (2,32, 3), 22 none, 88 forms two (7,37, 3), 77 forms one (33): 4+0+2+0+2+1=94 + 0 + 2 + 0 + 2 + 1 = 9. Bubble sort made 99 swaps in 2020 comparisons (five passes of 6,5,4,3,26, 5, 4, 3, 2 comparisons; the fifth pass made no swap and ended the sort), and insertion sort made 99 shifts in 1414 comparisons (9+6−19 + 6 - 1: the key 11 went to the front). Selection sort made 55 swaps in 2121 comparisons.

EX 3The reversed array is the worst case for all three

Medium

For n=8n = 8 and the array 8,7,…,18, 7, \ldots, 1 the table gives: bubble sort 2828 comparisons and 2828 swaps; insertion sort 2828 comparisons and 2828 shifts; selection sort 2828 comparisons but only 4=⌊n/2⌋4 = \lfloor n/2 \rfloor swaps (on this input each swap happens to put the smallest and the largest remaining key into place at once). So if the question asks "which algorithm performs the fewest swaps (writes) in the worst case", the answer is selection sort: at most n−1n - 1.

EX 4Nearly sorted input: O(nk)

Medium

Suppose every key is at most kk positions away from its place in the sorted order. Take a key xx at position ii; its final place is at least i−ki - k. A larger key yy that stands before xx, at a position j<ij < i, must end up after xx, so the final place of yy is also beyond i−ki - k; since yy moves at most kk places, j>i−2kj > i - 2k. So all keys larger than xx that stand before it lie among the 2k−12k - 1 positions just before ii, each key forms at most 2k−12k - 1 inversions with earlier keys, and I≤n(2k−1)I \le n(2k - 1). Insertion sort takes Θ(n+I)=O(nk)\Theta(n + I) = O(nk): linear for constant kk. Bubble sort with the early exit needs at most 2k2k passes (the passes are 1+1 + the largest number of larger keys to the left), so also O(nk)O(nk). Selection sort still takes Θ(n2)\Theta(n^2).

EX 5What do binary insertion sort and an adjacent-swap argument tell us?

GATE level

(a) Binary insertion sort finds each position with O(log⁡n)O(\log n) comparisons, so the number of comparisons is O(nlog⁡n)O(n \log n); but to make room the elements must be shifted, Θ(n)\Theta(n) per key in the worst case, so the running time is still Θ(n2)\Theta(n^2). (b) Any algorithm that sorts by swapping adjacent elements only: the reversed array has n(n−1)/2n(n-1)/2 inversions and each swap removes one, so Ω(n2)\Omega(n^2) swaps are needed in the worst case, and on average Ω(n2)\Omega(n^2) as well since the average number of inversions is n(n−1)/4n(n-1)/4.

Cheat box

Cheat box

  • Inversion: a pair i<ji < j with ai>aja_i > a_j. Range 00 to n(n−1)/2n(n-1)/2; average n(n−1)/4n(n-1)/4.
  • Bubble (early exit): swaps =I= I; comparisons n−1n - 1 (sorted) to n(n−1)/2n(n-1)/2; best Θ(n)\Theta(n); stable, in place. Without the early exit it is always n(n−1)/2n(n-1)/2 comparisons.
  • Selection: always n(n−1)/2n(n-1)/2 comparisons; at most n−1n - 1 swaps; Θ(n2)\Theta(n^2) in every case; in place; not stable (the long-distance swap).
  • Insertion: shifts =I= I; comparisons =I+(n−1)−f= I + (n - 1) - f (≤I+n−1\le I + n - 1); best Θ(n)\Theta(n), worst Θ(n2)\Theta(n^2); time Θ(n+I)\Theta(n + I); stable, in place, adaptive, online.
  • All three: worst and average Θ(n2)\Theta(n^2), space O(1)O(1).
  • Adjacent-swap sorts need Ω(I)\Omega(I) time, so Ω(n2)\Omega(n^2) on average.
  • Binary insertion sort: O(nlog⁡n)O(n \log n) comparisons, still Θ(n2)\Theta(n^2) shifts.

Traps and GATE patterns

Connections

The properties asked about on these sorts (stable, in place, adaptive) are compared with the fast sorts in stability and comparison of sorts. The fast sorts are merge sort, quicksort and heap sort; the reason nlog⁡nn \log n is the best possible for comparison sorts is in lower bound and linear-time sorts. The comparison counts here use the loop analysis of analysing loops.

PYQs

5 previous year questions from your book. I re-solved each one myself (4 by running or simulating, 1 by reasoning only); the book's key is shown next to my answer. All PYQs

GATE 2016 · Set 1 · MCQQ8 · page 109 of the book
The worst-case running time of Insertion sort,
Merge sort and Quick sort, respectively, are:
  • (a)θ(nlogn), θ(nlogn) and θ(n2)
  • (b)θ(n2) , θ(n2) and θ(nlogn)
  • (c)θ(n2), θ(nlogn) and θ(nlogn)
  • (d)θ(n2), θ(nlogn) and θ(n2)
GATE 2013 · MCQ · 1 markQ6 · page 144 of the book
Which one of the following is the tightest upper
bound that represents the number of swaps
required to sort n numbers using selection sort?
  • (a)O(log n)
  • (b)O(n)
  • (c)O(n log n)
  • (d)O(n2)
GATE 2025 · Set 1 · MCQ · 1 markQ6 · page 40 of the book
The pseudocode of a function fun() is given below:

fun(int A[0,...,n-1])
{
    for i=0 to n-2
    for j-0 to n-i-2
        if (A[j] > A[j+1])
        then swap A[j] and A[j+1]}
}

The pseudocode of a function fun() is given below:
fun(int A[0,...,n-1])
{
    for i=0 to n-2
    for j-0 to n-i-2
        if (A[j] > A[j+1])
        then swap A[j] and A[j+1]}
}
Let A[0, ... ,29] be an array storing 30 distinct integers
in descending order. The number of swap operations
that will be performed, if the function fun0 is called
with A[0, ... ,29] as argument, is ____ . (Answer in
integer)
GATE 2017 · NAT · 2 marksQ16 · page 42 of the book
Consider the following snippet of a C program.
Assume that swap(&x, &y)  exchanges the contents
of x and y.
int main ()  {
    int array[] = {3, 5, 1, 4, 6, 2};
    int done = 0;
    int i;
while (done = = 0)  {
     done = 1;
     for (i=0; i<=4; i++)  {
            if (array[i] < array[i+1])  {
               swap (&array[i], &array[i+1]);
               done = 0;
        }
}
for (i=5;  i>=1;  i- -) {
      if (array[i] > array[i-1])   {
      swap(&array[i], &array[i-1]);
      done=0;
       }
     }
}
printf("%d", array[3]);
}
The output of the program is ________.
GATE 2020 · MCQ · 1 markQ6 · page 56 of the book
What is the worst case time complexity of inserting n
elements into an empty linked list, if the linked list
needs to be maintained in sorted order?
  • (a)Θ(n log n)
  • (b)Θ(n2 )
  • (c)Θ(1).
  • (d)Θ(n)

Self-check

Question 1

How many swaps does bubble sort make on 3,2,13, 2, 1? How many shifts does insertion sort make?

Show answer

The inversions are (3,2),(3,1),(2,1)(3,2), (3,1), (2,1): 33. Both make 33.

Question 2

How many comparisons does selection sort make on an array of 1010 keys that is already sorted?

Show answer

10⋅9/2=4510 \cdot 9 / 2 = 45, the same as for any other input.

Question 3

Which of the three algorithms has a Θ(n)\Theta(n) best case?

Show answer

Insertion sort, and bubble sort with the early exit. Selection sort is Θ(n2)\Theta(n^2) in every case.

Question 4

Why does a sort that only swaps adjacent elements need Ω(n2)\Omega(n^2) time on average?

Show answer

Each adjacent swap removes at most one inversion, and a random array has n(n−1)/4n(n-1)/4 inversions on average.

Revision card

One-screen revision

  • Inversions II: bubble swaps =I= I, insertion shifts =I= I, insertion time Θ(n+I)\Theta(n + I), average I=n(n−1)/4I = n(n-1)/4.
  • Selection: n(n−1)/2n(n-1)/2 comparisons always, at most n−1n - 1 swaps, not stable.
  • Bubble (flag) and insertion: best Θ(n)\Theta(n), worst and average Θ(n2)\Theta(n^2), stable, in place.
  • Adjacent-swap sorts are Ω(n2)\Omega(n^2) on average; binary insertion sort still shifts Θ(n2)\Theta(n^2).

References

  • ·Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (3rd edition), Chapter 2 Getting Started (2.1 insertion sort) and Problem 2-2 (bubble sort), Problem 2-4 (inversions)
  • ·Sedgewick and Wayne, Algorithms (4th edition), Section 2.1 Elementary Sorts
  • ·Knuth, The Art of Computer Programming, Volume 3, Section 5.2.1 Sorting by insertion; 5.2.2 sorting by exchanging and selection

Verification

Every statement about counts was checked on EVERY permutation of up to 7 distinct keys by an independent Python implementation (bubble swaps = insertion shifts = inversions; selection n(n-1)/2 comparisons and at most n-1 swaps; the closed forms for the passes of bubble sort and the comparisons of insertion sort; average inversions n(n-1)/4). The table is the output of the C program on this page, recomputed in Python. The three animations are real runs of the C code of the page (scripts/gen-algo-panel-traces.ts). Textbook chapter references are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (loop counts, recurrences, merge and quicksort counts and averages, hash probes and load-factor formulas, DP tables, MST and shortest-path runs, Floyd matrices); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; No other defect found. Status raised to self-checked; verified needs a textbook or human check.

last verified: never