🫧 Bubble Sort¶
What You'll Learn
- ✅ What Bubble Sort is and how it works
- ✅ Basic and optimized implementations in Python
- ✅ Time and Space complexity analysis
- ✅ When to use (and NOT use) Bubble Sort
- ✅ Visualizing passes and swaps step-by-step
Bubble Sort is the classic "teaching" sorting algorithm — each pass bubbles the largest unsorted element to its correct position at the end of the array. It's rarely used in production, but understanding it builds the intuition for more advanced sorts like Insertion Sort and Quick Sort.
New to sorting algorithms?
Bubble Sort is the perfect starting point. It's slow, but every step is visible and logical. Nail this before moving to Merge Sort or Quick Sort.
Already know the basics?
Skip to the Optimized Version with the early-exit flag, then check the Complexity section to understand exactly when it's O(n) vs O(n²).
Keep in mind
Bubble Sort has O(n²) average and worst-case time complexity. Never use it on large datasets — Python's built-in sorted() uses Timsort (O(n log n)) and is always a better choice in practice.
How It Works¶
flowchart TD
A([Start]) --> B[i = 0]
B --> C{i less than n-1?}
C -->|No| Z([Array Sorted])
C -->|Yes| D[j = 0]
D --> E{j less than n-i-1?}
E -->|No| F[i = i + 1]
F --> C
E -->|Yes| G{arr-j greater than arr-j+1?}
G -->|No| H[j = j + 1]
G -->|Yes| I[Swap arr-j and arr-j+1]
I --> H
H --> E
1️⃣ Basic Bubble Sort¶
def bubble_sort(arr: list) -> list:
"""
Sorts arr in ascending order using Bubble Sort.
Modifies the list in-place and also returns it.
"""
n = len(arr)
for i in range(n - 1): # n-1 passes total
for j in range(n - i - 1): # Last i elements already sorted
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j] # Swap
return arr
# Example
arr = [64, 34, 25, 12, 22, 11, 90]
print(bubble_sort(arr))
Output:
Pythonic swap
arr[j], arr[j+1] = arr[j+1], arr[j] uses tuple unpacking — no temp variable needed. This is idiomatic Python.
2️⃣ Optimized Bubble Sort (Early Exit)¶
If the array becomes sorted before all passes complete, the basic version still wastes time. Adding a swapped flag cuts it short.
def bubble_sort_optimized(arr: list) -> list:
"""
Optimized Bubble Sort with early exit.
Best case O(n) when array is already sorted.
"""
n = len(arr)
for i in range(n - 1):
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 # No swaps → array is sorted, stop early
return arr
# Already sorted — exits after 1 pass
arr = [1, 2, 3, 4, 5]
print(bubble_sort_optimized(arr))
# Needs full sort
arr2 = [5, 3, 8, 1, 2]
print(bubble_sort_optimized(arr2))
Output:
3️⃣ Step-by-Step Trace¶
arr = [64, 34, 25, 12, 22]
Pass 1 (i=0): compare all adjacent pairs
[64, 34, 25, 12, 22]
64>34 → swap → [34, 64, 25, 12, 22]
64>25 → swap → [34, 25, 64, 12, 22]
64>12 → swap → [34, 25, 12, 64, 22]
64>22 → swap → [34, 25, 12, 22, 64] ← 64 bubbled to end ✅
Pass 2 (i=1): last 1 element sorted, compare up to index 3
[34, 25, 12, 22, 64]
34>25 → swap → [25, 34, 12, 22, 64]
34>12 → swap → [25, 12, 34, 22, 64]
34>22 → swap → [25, 12, 22, 34, 64] ← 34 in place ✅
Pass 3 (i=2):
[25, 12, 22, 34, 64]
25>12 → swap → [12, 25, 22, 34, 64]
25>22 → swap → [12, 22, 25, 34, 64] ← 25 in place ✅
Pass 4 (i=3):
[12, 22, 25, 34, 64]
12<22 → no swap ← 22 in place ✅
Final: [12, 22, 25, 34, 64] ✅
4️⃣ Memory Model¶
arr = [64, 34, 25, 12, 22]
[0] [1] [2] [3] [4]
Swap arr[0] and arr[1]:
┌─────────────────────────────┐
│ temp = arr[0] → temp=64 │ ← Python avoids this with tuple swap
│ arr[0] = arr[1] → 34 │
│ arr[1] = temp → 64 │
└─────────────────────────────┘
Python tuple swap (no temp variable):
arr[0], arr[1] = arr[1], arr[0]
RHS evaluated first → (34, 64) → unpacked into arr[0], arr[1]
Memory used: O(1) — only loop indices i, j and swapped flag
Original array modified in-place, no copy made.
NOT in-place (creates a new list): In-place (modifies original):
┌──────────────┐ ┌──────────────┐
│ original_arr │ ──────────────────────────▶│ original_arr │ (sorted ✅)
└──────────────┘ └──────────────┘
┌──────────────┐
│ sorted_arr │ (new allocation in memory)
└──────────────┘
Bubble Sort is IN-PLACE → space complexity O(1)
sorted() / sort() with key copies → O(n) space
5️⃣ Complexity Analysis¶
| Case | Input | Complexity | Explanation |
|---|---|---|---|
| Best | Already sorted | O(n) | Optimized version exits after 1 pass with no swaps |
| Average | Random order | O(n²) | ~n²/2 comparisons on average |
| Worst | Reverse sorted | O(n²) | Maximum swaps every pass |
| Aspect | Complexity | Reason |
|---|---|---|
| Auxiliary space | O(1) | Only a few variables (i, j, swapped) |
| In-place? | ✅ Yes | Sorts the original array, no copy |
| Stable? | ✅ Yes | Equal elements never swap — relative order preserved |
Why O(n²)?
For n elements, Pass 1 does n-1 comparisons, Pass 2 does n-2, ..., Pass n-1 does 1. Total = (n-1) + (n-2) + ... + 1 = n(n-1)/2 → O(n²)
6️⃣ Bubble Sort vs Other Sorts¶
| Algorithm | Best | Average | Worst | Space | Stable |
|---|---|---|---|---|---|
| Bubble Sort | O(n) | O(n²) | O(n²) | O(1) | ✅ |
| Selection Sort | O(n²) | O(n²) | O(n²) | O(1) | ❌ |
| Insertion Sort | O(n) | O(n²) | O(n²) | O(1) | ✅ |
| Merge Sort | O(n log n) | O(n log n) | O(n log n) | O(n) | ✅ |
| Quick Sort | O(n log n) | O(n log n) | O(n²) | O(log n) | ❌ |
| Timsort (Python) | O(n) | O(n log n) | O(n log n) | O(n) | ✅ |
✅ Use when: - Learning sorting algorithms for the first time - Array is nearly sorted (optimized version shines) - Dataset is tiny (< 10 elements) - Simplicity and readability are more important than speed
❌ Avoid when:
- Dataset is large (use sorted() or .sort())
- Performance matters at all
- You're writing production code
✅ Quick Reference Summary¶
| Topic | Key Point |
|---|---|
| Strategy | Repeatedly swap adjacent elements if out of order |
| Passes needed | At most n - 1 passes for n elements |
| Time — Best | O(n) — with early exit on already-sorted input |
| Time — Average/Worst | O(n²) |
| Space | O(1) — in-place |
| Stable? | ✅ Yes — equal elements keep their original order |
| Optimization | swapped flag for early exit |
| Python swap | a, b = b, a — no temp variable needed |
| Use in prod? | ❌ — use sorted() or .sort() (Timsort) instead |
| Good for | Teaching, nearly-sorted data, tiny arrays |