Skip to content
Ameen Abozaid
PublishedBEG-017-L04

Bubble sort

  • Lesson 4 of 7
  • 10 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

Binary search needs sorted data, so now we learn how to sort. Bubble sort is the easiest sorting algorithm to understand: compare two neighbours, swap them if they are in the wrong order, and sweep along the array. After one sweep, the largest value has "bubbled" to the end. Repeat the sweeps and the whole array ends up sorted.

Bubble sort is slow on big arrays and real programs rarely use it. We study it because it is small, it shows how nested loops do real work, and it is the best practice for tracing an algorithm by hand, which is exactly what the assessment for this module asks you to do.

Why this matters: sorting questions are standard in junior interviews, and being able to trace a sort pass by pass shows that you understand loops and arrays rather than memorising code.

This lesson builds on binary search, nested loops, arrays, and swapping two values with a temporary variable.

Learning Objectives

By the end of this lesson you will be able to:

  • Describe bubble sort as repeated passes that swap out-of-order neighbours.
  • Explain what one pass guarantees about the largest value.
  • Implement bubble sort for an int[] with two nested loops.
  • Trace bubble sort on a small array, showing the array after each pass.
  • Explain why the inner loop gets shorter on every pass.
  • Add an early exit using a swapped flag.
  • State roughly how many comparisons bubble sort makes and why it is slow.

Written Explanation

The idea

Stand five people in a line and ask each neighbouring pair, from left to right: "If the person on the left is taller, swap places." After one sweep the tallest person has been carried all the way to the right end, because every time they met someone shorter, they swapped forward. Repeat the sweep, and the second tallest settles next to the end, and so on.

For {4, 2, 3}:

  • Compare 4 and 2: out of order, swap, giving {2, 4, 3}.
  • Compare 4 and 3: out of order, swap, giving {2, 3, 4}.

One pass has moved 4, the largest, to the end. Here the array happens to be sorted already, but the algorithm does not know that yet.

The swap

Swapping two array positions needs a temporary variable. Writing a[i] = a[i + 1]; a[i + 1] = a[i]; would lose the original a[i]. The correct pattern:

java
int t = a[i];
a[i] = a[i + 1];
a[i + 1] = t;

The code

java
static void bubbleSort(int[] a) {
    for (int pass = 0; pass < a.length - 1; pass++) {
        for (int i = 0; i < a.length - 1 - pass; i++) {
            if (a[i] > a[i + 1]) {
                int t = a[i]; a[i] = a[i + 1]; a[i + 1] = t;
            }
        }
    }
}

The method returns void because it changes the array it is given. The method receives the same array the caller has, not a copy, so the caller sees the sorted array. If you need the original order kept, copy the array first.

Why these bounds?

  • The inner loop compares a[i] with a[i + 1], so i must stop at a.length - 2, hence i < a.length - 1 for the first pass. Without that limit the code would read past the end.
  • After each pass the last pass + 1 positions already hold the largest values in their final places. The - pass makes the inner loop skip them, so it gets shorter each time.
  • The outer loop needs at most a.length - 1 passes: once all but one element are in place, the last one must be in place too.

Tracing it

To trace {7, 3, 9, 1, 5}, write the array after each pass. A version of the method that prints the array and a swapped flag after each pass gives:

text
after pass 1: [3, 7, 1, 5, 9] swapped=true
after pass 2: [3, 1, 5, 7, 9] swapped=true
after pass 3: [1, 3, 5, 7, 9] swapped=true
after pass 4: [1, 3, 5, 7, 9] swapped=false

Reading pass 1 in detail:

compareactionarray
7 and 3swap3 7 9 1 5
7 and 9no swap3 7 9 1 5
9 and 1swap3 7 1 9 5
9 and 5swap3 7 1 5 9

The 9, the largest, is now at the end, as promised. Pass 2 does the same for the rest (excluding the last place), and so on.

Stopping early

If a whole pass makes no swaps, every neighbour pair is in order, so the array is sorted and we can stop. A boolean flag does this:

java
for (int pass = 0; pass < a.length - 1; pass++) {
    boolean swapped = false;
    for (int i = 0; i < a.length - 1 - pass; i++) {
        if (a[i] > a[i + 1]) {
            int t = a[i]; a[i] = a[i + 1]; a[i + 1] = t;
            swapped = true;
        }
    }
    if (!swapped) return;
}

On an array that is already sorted this finishes after a single pass.

How slow is it?

With the shrinking inner loop, the passes compare n - 1, then n - 2, and so on down to 1 pair. For 5 elements that is 4 + 3 + 2 + 1 = 10 comparisons; for 40 elements it is 780. The total grows roughly like n × n: double the data and the work about quadruples. A thousand elements already means about half a million comparisons. This is why bubble sort is a teaching tool and not a production choice.

Examples

Example 1: tracing a tiny array (simple)

{4, 2, 3} with the flag version:

text
after pass 1: [2, 3, 4] swapped=true
after pass 2: [2, 3, 4] swapped=false

Pass 1 compares 4 > 2 (swap) and then 4 > 3 (swap), so the 4 travels to the end. Pass 2 compares only the first pair, 2 > 3, finds nothing to swap, and the flag tells us we are done.

Example 2: sorting exam scores and checking the result (practical)

java
int[] sc = {88, 61, 95, 70, 61};
bubbleSort(sc);
System.out.println(Arrays.toString(sc));

Output:

text
[61, 61, 70, 88, 95]

Repeated values (61 twice) are no problem: the test a[i] > a[i + 1] is false for equal neighbours, so they are not swapped. Edge cases also work, with no special code. For {} and {9} the outer loop does not run, since a.length - 1 is -1 and 0, and the arrays are printed unchanged: [] [9]. A good habit is to call isSorted from the first lesson on the result in a test.

Example 3: sorting in the other direction (realistic)

A leaderboard needs the highest score first. The only change is the comparison:

java
if (a[i] < a[i + 1]) {   // was >
    int t = a[i]; a[i] = a[i + 1]; a[i + 1] = t;
}

Run on {2, 9, 4, 7} this prints:

text
[9, 7, 4, 2]

Now the smallest value bubbles to the end of each pass. Changing which direction counts as "out of order" is the usual way to flip a sort. (In real code you would normally use Arrays.sort and then deal with the order, which you will meet in later modules.)

Backend Insight

You will almost never write bubble sort in a service. The Java library's Arrays.sort is tested and fast; its documentation describes a Dual-Pivot Quicksort for int[], advertised as O(n log(n)) on all data sets, and notes that this is an implementation note and may change. What you do take from bubble sort is the cost model: nested loops over the same data mean work that grows like n × n. That pattern is a classic cause of slow endpoints, for example when code compares every order with every other order for 50,000 orders. Spotting a loop inside a loop over the same data is a basic review skill.

Also remember the contract: sorting methods that change the array in place change it for every part of the program that holds a reference to it. Copy first when others depend on the original order.

Common Mistakes

Mistake 1: Swapping without a temporary variable

Assigning a[i] = a[i + 1]; a[i + 1] = a[i]; leaves both positions with the same value. Use t.

Mistake 2: Off-by-one in the inner loop

  • i < a.length makes a[i + 1] read past the end and throws ArrayIndexOutOfBoundsException on the last pair.
  • i < a.length - 1 - pass is correct. Forgetting - pass still gives the right answer, but does wasted comparisons.

Mistake 3: Using >= for the comparison

a[i] >= a[i + 1] swaps equal neighbours. The result is still sorted, but equal values are swapped for no reason, and the swapped flag is set even when nothing really changed, so the early exit can stop working on arrays with duplicates.

Mistake 4: Thinking one pass sorts everything

One pass only fixes one value, the largest of the unsorted part. Several passes are needed. Tracing a short array shows this immediately.

Exercises

Try each exercise yourself before opening the solutions.

Level A: Foundation

A1. After the first pass of bubble sort, where is the largest value?

Show solution A1

A1. At the end (the last position of the array).

A2. Write the three lines that swap a[i] and a[i + 1].

Show solution A2

A2.

java
int t = a[i];
a[i] = a[i + 1];
a[i + 1] = t;

A3. How many comparisons does bubble sort (with the shrinking inner loop and no early exit) make on 5 elements?

Show solution A3

A3. 4 + 3 + 2 + 1 = 10.

Level B: Application

B1. Trace bubble sort on {6, 2, 5, 1} and write the array after each pass.

Show solution B1

B1.

text
after pass 1: [2, 5, 1, 6]
after pass 2: [2, 1, 5, 6]
after pass 3: [1, 2, 5, 6]

In pass 1 the 6 moves right past 2, 5 and 1 (it swaps with each). In pass 2 the 5 settles, and in pass 3 the pair 2, 1 is swapped. After three passes (n - 1) the array is sorted.

B2. Trace {2, 1, 3, 4} using the early-exit version. After which pass does it stop?

Show solution B2

B2. The array after pass 1 is [1, 2, 3, 4], with a swap (2 and 1). Pass 2 makes no swap, so swapped is false and the method stops after pass 2.

B3. What should the code print for bubbleSort on an empty array followed by Arrays.toString? Explain why no exception occurs.

Show solution B3

B3. It prints []. For an empty array a.length - 1 is -1, so the outer loop condition pass < -1 is false at once and nothing runs.

Level C: Challenge

C1. Add counters for comparisons and swaps to the version without the early exit, and report them for {5, 1, 4, 2, 8}, for the already sorted {1, 2, 3, 4, 5}, and for the reversed {5, 4, 3, 2, 1}.

Show solution C1

C1. Results from running the code on JDK 21:

InputComparisonsSwaps
{5, 1, 4, 2, 8}104
{1, 2, 3, 4, 5}100
{5, 4, 3, 2, 1}1010

Without the early exit every case makes 10 comparisons, so the number of comparisons does not depend on the data, while the number of swaps does. With the early exit the sorted array would need only 4 comparisons (one pass, as C2 explains).

C2. Explain why the early-exit version needs only n - 1 comparisons for an already sorted array of n elements.

Show solution C2

C2. The first pass compares all n - 1 neighbour pairs and finds nothing out of order, so swapped stays false and the method returns right away.

Mini Challenge

Take the array {9, 4, 7, 1, 8, 3}. Without running code, write the array after each pass of bubble sort and count the comparisons and swaps. Then write the program, add counters, and check that your hand trace matches the output. Finally change the comparison to sort descending and predict the first pass before running it.

Check your work: your hand trace and the program agree on every pass, and isSorted confirms the result.

Summary & Next Lesson

Key takeaways

  • Bubble sort repeatedly compares neighbours and swaps them if they are out of order.
  • After one pass the largest remaining value sits at the end, so the inner loop can shrink by one each pass.
  • Swapping needs a temporary variable.
  • A pass with no swaps means the array is sorted, so a swapped flag allows an early exit.
  • It makes about n × n / 2 comparisons: double the data, about four times the work. Easy to understand, slow for large data.

Next lesson

The next lesson, "Selection sort", sorts a different way: instead of swapping neighbours many times, it finds the smallest remaining value and places it directly in its final position.

Self-check

  1. What does one pass of bubble sort guarantee?
  2. Why does the inner loop stop at a.length - 1 - pass?
  3. How do you swap two array elements?
  4. What does a pass with no swaps tell you?
  5. If the array doubles in size, about how does the work change?
Show answers
  1. The largest value of the unsorted part is now at the end of it.
  2. The last pass positions are already in their final places, and a[i + 1] must not go past the end.
  3. Save one value in a temporary variable, copy the other over it, then copy the temporary into the other position.
  4. The array is sorted, so the sort can stop.
  5. It roughly quadruples.

References

  • Oracle, Arrays (Java SE 21 API). Arrays.sort(int[]) sorts into ascending order, and the implementation note names a Dual-Pivot Quicksort with O(n log(n)) performance, while stating that implementation notes are not part of the specification.
  • The pass-by-pass traces, counts, and outputs in this lesson come from compiling and running the code on JDK 21. The comparison count n(n - 1) / 2 follows from adding n - 1 down to 1.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.