Data Structure 📂 Sorting & Order Statistics · 7 of 10 29 min read

Bubble Sort, Hands-On

An interactive, beginner-friendly bubble sort tutorial in Python where the animation and the code run in lockstep — each executing line lights up as the bars compare, swap, and lock into place. Covers the soda-bubble analogy, a full line-by-line breakdown, a hand-trace, naive vs optimised versions, time/space complexity, benefits, real use cases, and a comparison against other sorts. Dark- and light-theme ready

Section 01

The Story That Explains Bubble Sort

Bubbles Rising in a Glass of Soda
Pour a fresh glass of soda and watch closely. The biggest bubbles don't stay at the bottom — they push past the smaller ones and rise to the top fastest. Tiny bubbles drift up slowly; the giants race ahead.

Bubble Sort works the exact same way. In every pass through the list, the largest remaining value "bubbles up" to its correct position at the end, one neighbour-swap at a time. Do this enough times and every value floats to where it belongs. That rising-bubble image is literally where the algorithm gets its name.

Bubble Sort is the friendliest sorting algorithm to learn first. It only ever does one simple thing: look at two side-by-side items, and if the left one is bigger, swap them. Repeat across the whole list, again and again, until nothing needs swapping. No clever tricks, no recursion, no extra memory — just compare-and-swap.

🍂
The Core Insight

If, on a full sweep through the list, you make zero swaps, the list must already be sorted — so you can stop early. That single observation turns the naive version into the optimised version you'll build below.


Section 02

How It Works — One Pass at a Time

A "pass" means walking from the start of the list to the end, comparing each adjacent pair. Here is exactly what happens, step by step:

📊 Anatomy of a Single Pass
Step 1
Start at the first pair: compare arr[0] and arr[1].
Step 2
If the left value is greater than the right, swap them. Otherwise leave them.
Step 3
Slide one position right and compare the next pair. Keep going to the end.
Result
After the pass, the largest unsorted value has bubbled to the end — locked in place.
Repeat
Run more passes. Each pass needs one fewer comparison, because the tail is already sorted.
2
5
9
1
4

The largest value (9, in red) is the one that bubbles to the top fastest each pass.


Section 03

Hands-On: Watch Each Line Run

This is the heart of the tutorial. Press Play (or Step) and watch two things move together: the bars on the left rearrange, while the exact Python line doing that work lights up on the right. The status box tells you, in plain English, what that line is doing right now.

⚡ Live Bubble Sort — code ↔ animation optimised version
Press Play or Step ▶ to begin. The highlighted line on the right is the one running.
0Pass
0Comparisons
0Swaps
def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        swapped = False
        for j in range(n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swapped = True
        if not swapped:
            break
    return arr
💡
What the colours mean

Amber bars = the pair currently being compared. Red bars = a swap is happening. Green bars = locked in their final sorted position. The amber-highlighted code line is the instruction the interpreter is executing at that instant.


Section 04

The Code, Line by Line — "This Line Does That"

Same program as the animation, now annotated. Each line maps to exactly one job the visualiser highlights.

def bubble_sort(arr):
    n = len(arr)                      # how many items to sort
    for i in range(n):                  # outer loop -> one pass each time
        swapped = False                 # did we change anything this pass?
        for j in range(n - i - 1):        # inner loop, skips the sorted tail
            if arr[j] > arr[j + 1]:       # left bigger than right?
                arr[j], arr[j+1] = arr[j+1], arr[j]   # swap the pair
                swapped = True          # remember we made a change
        if not swapped:               # no swaps in a whole pass?
            break                     # already sorted - stop early!
    return arr                        # the sorted list
🔎 What Each Line Is Responsible For
Line 1
def bubble_sort(arr) — defines the function and receives the list to sort.
Line 2
n = len(arr) — stores the length so the loops know their bounds.
Line 3
for i in range(n) — the outer loop. Each iteration is one full pass.
Line 4
swapped = False — resets the "did anything move?" flag at the start of every pass.
Line 5
for j in range(n - i - 1) — the inner loop. The - i skips the already-sorted tail, so each pass is shorter.
Line 6
if arr[j] > arr[j+1] — the comparison. Are this pair out of order?
Line 7
arr[j], arr[j+1] = ... — the swap, done in one clean Python tuple assignment (no temp variable).
Line 8
swapped = True — records that this pass changed something.
Line 9
if not swapped — the early-exit check after each pass.
Line 10
break — if a whole pass made no swaps, the list is sorted, so leave immediately.
Line 11
return arr — hand back the sorted list.
OUTPUT — bubble_sort([5, 2, 9, 1, 7, 3])
[1, 2, 3, 5, 7, 9]

Section 05

A Full Hand-Trace

Let's sort [5, 1, 4, 2, 8] by hand so you can see what the animation computes. Bold = the pair being compared; arrows show a swap.

PassComparisonList StateAction
15 & 15 1 4 2 8Swap → 1 5 4 2 8
15 & 41 5 4 2 8Swap → 1 4 5 2 8
15 & 21 4 5 2 8Swap → 1 4 2 5 8
15 & 81 4 2 5 8No swap (8 locked)
21 & 41 4 2 5 8No swap
24 & 21 4 2 5 8Swap → 1 2 4 5 8
24 & 51 2 4 5 8No swap (5 locked)
3full pass1 2 4 5 80 swaps → break early ✓
🎉
The Early-Exit Payoff

Pass 3 made zero swaps, so the optimised version stops without doing a wasted fourth pass. On an already-sorted list, this makes Bubble Sort finish in a single pass — its best case of O(n).


Section 06

Naive vs Optimised

❌ Naive — always n passes
InputPasses done
Randomn
Already sortedn (wasteful!)
Best caseO(n²)
Stops early?Never
✅ Optimised — swapped flag
InputPasses done
Random≤ n
Already sorted1 only ✓
Best caseO(n)
Stops early?Yes

The Naive Version (for comparison)

def bubble_sort_naive(arr):
    n = len(arr)
    for i in range(n):
        for j in range(n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return arr   # no swapped flag -> never quits early

Section 07

Time & Space Complexity

CaseWhenComparisonsComplexity
BestAlready sortedn - 1O(n)
AverageRandom order~ n²/2O(n²)
WorstReverse sortedn(n-1)/2O(n²)
SpaceAnyin-placeO(1)
Stable?Equal keys keep orderYes ✓
⚠️
Why It's O(n²)

You run up to n passes, and each pass compares up to n pairs. n × n = . Doubling the list size roughly quadruples the work. Sorting 1,000 items takes ~1,000,000 comparisons — which is why Bubble Sort is a teaching tool, not a production sorter for large data.


Section 08

Benefits

🧠
Dead Simple to Understand
teaching favourite
Compare-and-swap is the entire idea. It's the textbook first algorithm because the logic fits in your head with no recursion or auxiliary structures.
💾
In-Place — O(1) Memory
no extra arrays
It sorts by swapping inside the original list. No copies, no buffers. Handy on memory-tight hardware like small microcontrollers.
⚖️
Stable Sort
preserves order
Items with equal keys keep their original relative order — important when sorting records by a secondary field.
Best Case O(n)
detects sorted input
With the swapped flag, a nearly-sorted or fully-sorted list is confirmed in a single pass and exits immediately.
🔍
Easy to Debug
predictable
The list state after each pass is fully predictable, making it ideal for visualisations, step-debuggers, and exam questions.
📝
Tiny Code Footprint
~6 lines
The whole algorithm is a handful of lines. Quick to type, quick to verify — perfect for whiteboard interviews and quick scripts on tiny inputs.

Section 09

When To Use It — and When Not To

Teaching & Learning
The clearest way to introduce sorting, loops, swaps, and Big-O. Its visual nature makes it perfect for animations like the one above.
classrooms, demos
Tiny Data Sets
For 5–20 items the O(n²) penalty is invisible. The simplicity can outweigh raw speed for trivial inputs.
n < ~20
Nearly-Sorted Lists
With the swapped flag it shines on data that's already almost in order — it confirms sortedness in roughly one pass.
live sensor streams
Large Data Sets
At thousands+ of elements the n² comparisons make it painfully slow. Reach for Merge Sort, Quick Sort, or Timsort instead.
n > ~1,000
Performance-Critical Code
Production paths should use the language's built-in sort. Python's sorted() / list.sort() use Timsort — far faster.
backend, real-time
Many Costly Swaps
Bubble Sort can perform many swaps. If moving an element is expensive (huge objects), Selection Sort minimises swaps instead.
large record moves

Section 10

Bubble Sort vs Other Simple Sorts

AlgorithmBestAverage / WorstSpaceStableNotes
Bubble SortO(n)O(n²)O(1)YesSimplest; detects sorted input
Selection SortO(n²)O(n²)O(1)NoFewest swaps
Insertion SortO(n)O(n²)O(1)YesGreat on nearly-sorted data
Merge SortO(n log n)O(n log n)O(n)YesReliable, needs extra memory
Quick SortO(n log n)O(n²) worstO(log n)NoFast in practice

Section 11

Golden Rules

🥤 Bubble Sort — Things to Remember
1
Always include the swapped flag. It costs one boolean and turns the best case from O(n²) into O(n) by exiting early on sorted data.
2
The inner loop runs to n - i - 1, not n - 1. The - i skips the already-sorted tail and is the reason later passes are shorter.
3
Use Python's tuple swap a, b = b, a — no temporary variable needed, and it reads exactly like the intent.
4
Bubble Sort is stable only if you swap on > (strictly greater), never on >=. Swapping equal items would scramble their original order.
5
It's a learning tool, not a production sorter. For real work use sorted() or list.sort() — Python's Timsort is dramatically faster.
6
Remember the name: the largest unsorted value "bubbles up" to the end on every pass. If you can picture that, you never need to memorise the code.