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 . 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 so that . 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 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 with . A sorted array has inversions, a strictly decreasing array has . The number of inversions measures how far an array is from sorted. Averaged over all orderings of distinct keys it is .
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 the largest 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 runC source
Array a (green = final position)
Counts
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.
Selection sort. Find the smallest key of the unsorted part and swap it into the first unsorted position. After pass the 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 runC source
Array a (green = final position)
Counts
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.
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 runC source
Array a (green = sorted prefix cells not being touched)
Counts
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.
Measured counts. The C program below runs all three on five inputs of 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 0Elementary sorts on 8 keys, measured: start
Computed by a script, not typed| input | inversions | bubble: comparisons | bubble: swaps | selection: comparisons | selection: swaps | insertion: comparisons | insertion: 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.
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 swaps, where 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 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 keys takes comparisons however they are ordered, so the total is always ( for in every row). It makes at most swaps, the fewest of the three, which matters when moving a record is expensive.
- Bubble sort with the early exit makes comparisons on a sorted array (one pass) and on a reversed one. The number of passes is the largest number of larger keys to the left of any key (at most ), 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 , where is the number of keys that are smaller than everything before them: between (sorted) and (reversed). It is adaptive: its time is .
- Worst case for all three is ; best case is for bubble sort with the early exit and for insertion sort, and for selection sort. Average case over random permutations of distinct keys: swaps or shifts, so 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 . But the shifts remain , so the time is still 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 inversions therefore needs at least such swaps. Since a random array has inversions on average, any sort that moves keys only by adjacent swaps takes 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
EasyStart with . Compare neighbours left to right: swap ; swap ; swap ; no swap; swap ; swap . That was comparisons and swaps; the largest key, , is at its final place. The animation shows the same pass.
EX 2Inversions equal the swaps
EasyFor the inversions are: forms four (), none, forms two (), none, forms two (), forms one (): . Bubble sort made swaps in comparisons (five passes of comparisons; the fifth pass made no swap and ended the sort), and insertion sort made shifts in comparisons (: the key went to the front). Selection sort made swaps in comparisons.
EX 3The reversed array is the worst case for all three
MediumFor and the array the table gives: bubble sort comparisons and swaps; insertion sort comparisons and shifts; selection sort comparisons but only 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 .
EX 4Nearly sorted input: O(nk)
MediumSuppose every key is at most positions away from its place in the sorted order. Take a key at position ; its final place is at least . A larger key that stands before , at a position , must end up after , so the final place of is also beyond ; since moves at most places, . So all keys larger than that stand before it lie among the positions just before , each key forms at most inversions with earlier keys, and . Insertion sort takes : linear for constant . Bubble sort with the early exit needs at most passes (the passes are the largest number of larger keys to the left), so also . Selection sort still takes .
EX 5What do binary insertion sort and an adjacent-swap argument tell us?
GATE level(a) Binary insertion sort finds each position with comparisons, so the number of comparisons is ; but to make room the elements must be shifted, per key in the worst case, so the running time is still . (b) Any algorithm that sorts by swapping adjacent elements only: the reversed array has inversions and each swap removes one, so swaps are needed in the worst case, and on average as well since the average number of inversions is .
Cheat box
Cheat box
- Inversion: a pair with . Range to ; average .
- Bubble (early exit): swaps ; comparisons (sorted) to ; best ; stable, in place. Without the early exit it is always comparisons.
- Selection: always comparisons; at most swaps; in every case; in place; not stable (the long-distance swap).
- Insertion: shifts ; comparisons (); best , worst ; time ; stable, in place, adaptive, online.
- All three: worst and average , space .
- Adjacent-swap sorts need time, so on average.
- Binary insertion sort: comparisons, still shifts.
Traps and GATE patterns
Connections
Builds on
Across subjects
No cross-subject links yet.
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 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
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)
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)
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)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 ________.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 ? How many shifts does insertion sort make?
Show answerHide
The inversions are : . Both make .
Question 2
How many comparisons does selection sort make on an array of keys that is already sorted?
Show answerHide
, the same as for any other input.
Question 3
Which of the three algorithms has a best case?
Show answerHide
Insertion sort, and bubble sort with the early exit. Selection sort is in every case.
Question 4
Why does a sort that only swaps adjacent elements need time on average?
Show answerHide
Each adjacent swap removes at most one inversion, and a random array has inversions on average.
Revision card
One-screen revision
- Inversions : bubble swaps , insertion shifts , insertion time , average .
- Selection: comparisons always, at most swaps, not stable.
- Bubble (flag) and insertion: best , worst and average , stable, in place.
- Adjacent-swap sorts are on average; binary insertion sort still shifts .
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
My notes (saved in this browser)