Bubble sort
Bubble sort repeatedly walks the array and swaps neighbours that are out of order. After each pass the largest remaining value has bubbled to the end. It is rarely used in production, but it is the clearest introduction to sorting invariants.
Learning goals
- Trace bubble sort pass by pass
- Add the early-exit optimisation and explain its O(n) best case
- Count comparisons and swaps and relate swaps to inversions
- Explain why bubble sort is stable
Prerequisites
Repeated adjacent swaps
Compare arr[j] with arr[j + 1] and swap them if the left one is larger. One full pass carries the largest value to the right end, like a bubble rising.
Invariant: after pass p, the last p + 1 positions hold the largest values in their final order, so the inner loop can stop at n - 1 - pass.
Bubble sort
Interactive · edit the inputint[] arr · length 7
Repeatedly compare neighbours and swap them when they are out of order. Large values bubble to the right.
1public static void bubbleSort(int[] arr) {2int n = arr.length;3for (int pass = 0; pass < n - 1; pass++) {4boolean swapped = false;5for (int j = 0; j < n - 1 - pass; j++) {6if (arr[j] > arr[j + 1]) {7int tmp = arr[j];8arr[j] = arr[j + 1];9arr[j + 1] = tmp;10swapped = true;11}12}13if (!swapped) break; // early exit: already sorted14}15}
Live state
n
7
Execution trace (dry run table)Show 43 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | n=7 | Repeatedly compare neighbours and swap them when they are out of order. Large values bubble to the right. |
| 2 | L4 | n=7 pass=0 swapped=false | Pass 1: after it, index 6 will hold its final value. |
| 3 | L6 | pass=0 j=0 swapped=false | Out of order, swap them. |
| 4 | L8 | pass=0 j=0 swapped=true | Swapped: arr[0] = 27, arr[1] = 38. |
| 5 | L6 | pass=0 j=1 swapped=true | Already in order, leave them. Equal values never swap, which keeps bubble sort stable. |
| 6 | L6 | pass=0 j=2 swapped=true | Out of order, swap them. |
| 7 | L8 | pass=0 j=2 swapped=true | Swapped: arr[2] = 3, arr[3] = 43. |
| 8 | L6 | pass=0 j=3 swapped=true | Out of order, swap them. |
| 9 | L8 | pass=0 j=3 swapped=true | Swapped: arr[3] = 9, arr[4] = 43. |
| 10 | L6 | pass=0 j=4 swapped=true | Already in order, leave them. Equal values never swap, which keeps bubble sort stable. |
| 11 | L6 | pass=0 j=5 swapped=true | Out of order, swap them. |
| 12 | L8 | pass=0 j=5 swapped=true | Swapped: arr[5] = 10, arr[6] = 82. |
| 13 | L13 | pass=0 swapped=true | 82 is now fixed at index 6. Swaps happened, so run another pass. |
| 14 | L4 | n=7 pass=1 swapped=false | Pass 2: after it, index 5 will hold its final value. |
| 15 | L6 | pass=1 j=0 swapped=false | Already in order, leave them. Equal values never swap, which keeps bubble sort stable. |
| 16 | L6 | pass=1 j=1 swapped=false | Out of order, swap them. |
| 17 | L8 | pass=1 j=1 swapped=true | Swapped: arr[1] = 3, arr[2] = 38. |
| 18 | L6 | pass=1 j=2 swapped=true | Out of order, swap them. |
| 19 | L8 | pass=1 j=2 swapped=true | Swapped: arr[2] = 9, arr[3] = 38. |
| 20 | L6 | pass=1 j=3 swapped=true | Already in order, leave them. Equal values never swap, which keeps bubble sort stable. |
| 21 | L6 | pass=1 j=4 swapped=true | Out of order, swap them. |
| 22 | L8 | pass=1 j=4 swapped=true | Swapped: arr[4] = 10, arr[5] = 43. |
| 23 | L13 | pass=1 swapped=true | 43 is now fixed at index 5. Swaps happened, so run another pass. |
| 24 | L4 | n=7 pass=2 swapped=false | Pass 3: after it, index 4 will hold its final value. |
| 25 | L6 | pass=2 j=0 swapped=false | Out of order, swap them. |
| 26 | L8 | pass=2 j=0 swapped=true | Swapped: arr[0] = 3, arr[1] = 27. |
| 27 | L6 | pass=2 j=1 swapped=true | Out of order, swap them. |
| 28 | L8 | pass=2 j=1 swapped=true | Swapped: arr[1] = 9, arr[2] = 27. |
| 29 | L6 | pass=2 j=2 swapped=true | Already in order, leave them. Equal values never swap, which keeps bubble sort stable. |
| 30 | L6 | pass=2 j=3 swapped=true | Out of order, swap them. |
| 31 | L8 | pass=2 j=3 swapped=true | Swapped: arr[3] = 10, arr[4] = 38. |
| 32 | L13 | pass=2 swapped=true | 38 is now fixed at index 4. Swaps happened, so run another pass. |
| 33 | L4 | n=7 pass=3 swapped=false | Pass 4: after it, index 3 will hold its final value. |
| 34 | L6 | pass=3 j=0 swapped=false | Already in order, leave them. Equal values never swap, which keeps bubble sort stable. |
| 35 | L6 | pass=3 j=1 swapped=false | Already in order, leave them. Equal values never swap, which keeps bubble sort stable. |
| 36 | L6 | pass=3 j=2 swapped=false | Out of order, swap them. |
| 37 | L8 | pass=3 j=2 swapped=true | Swapped: arr[2] = 10, arr[3] = 27. |
| 38 | L13 | pass=3 swapped=true | 27 is now fixed at index 3. Swaps happened, so run another pass. |
| 39 | L4 | n=7 pass=4 swapped=false | Pass 5: after it, index 2 will hold its final value. |
| 40 | L6 | pass=4 j=0 swapped=false | Already in order, leave them. Equal values never swap, which keeps bubble sort stable. |
| 41 | L6 | pass=4 j=1 swapped=false | Already in order, leave them. Equal values never swap, which keeps bubble sort stable. |
| 42 | L13 | pass=4 swapped=false | This pass made no swaps, so the array is sorted. Exit early: this is why the best case is O(n). |
| 43 | L15 | Sorted with 20 comparisons, 11 swaps. |
Early-exit optimization
If a whole pass makes no swaps, every neighbouring pair is in order, so the array is sorted. A swapped flag lets the algorithm stop immediately.
Without the flag, bubble sort always does n(n-1)/2 comparisons. With it, already sorted input takes a single pass of n - 1 comparisons. Run the “Already sorted” preset to see this.
Dry run
Sorting [5, 1, 4, 2, 8]. Each row is one pass; ↔ marks a swap.
- Total: 9 comparisons and 4 swaps.
- 4 swaps equals the number of inversions in the input: (5,1), (5,4), (5,2), (4,2).
| Pass | Comparisons in order | Array after pass | Swaps |
|---|---|---|---|
| 1 | 5↔1, 5↔4, 5↔2, 5 vs 8 | [1, 4, 2, 5, 8] | 3 |
| 2 | 1 vs 4, 4↔2, 4 vs 5 | [1, 2, 4, 5, 8] | 1 |
| 3 | 1 vs 2, 2 vs 4 | [1, 2, 4, 5, 8] | 0, early exit |
Complexity
Every swap fixes exactly one inversion, so swaps equal the number of inversions: 0 for sorted input, n(n-1)/2 for reverse sorted input.
Comparisons are n(n-1)/2 in the worst case. With early exit, the best case is one pass.
Best
O(n)
Average
O(n²)
Worst
O(n²)
Space
O(1)
Stability
Bubble sort swaps only when arr[j] > arr[j + 1], strictly greater. Equal neighbours are never swapped, so equal keys never pass each other and the sort is stable.
Changing the test to >= would still sort correctly but would break stability. Try the “Duplicates” preset and watch the a/b tags.
Practice ladder
Now try it yourself
Easy
- Bubble sort descending
- Count the swaps bubble sort makes on an input
Medium
- Implement the last-swap-position optimisation
- Bubble sort a singly linked list by swapping values
Advanced
- Prove that bubble sort's swap count equals the inversion count
- Find the minimum adjacent swaps to sort a permutation
Key takeaways
- Adjacent swaps move the largest remaining value to the end on each pass.
- Early exit gives an O(n) best case; average and worst are O(n²).
- Strict comparison keeps it stable; swaps equal the number of inversions.