Bubble Sort — Specification¶
Algorithm Specification — Reference Standard
Source: Knuth, The Art of Computer Programming, Vol. 3 — Section 5.2.2 Source: CLRS, Introduction to Algorithms — Problem 2-2
Table of Contents¶
- Algorithm Reference
- API / Interface Reference
- Core Rules and Behavioral Specification
- Schema / Parameters Reference
- Behavioral Specification
- Edge Cases
- Complexity Compatibility Matrix
- Reference Implementations
- Compliance & Best Practices Checklist
- Related Documentation
1. Algorithm Reference¶
| Property | Value |
|---|---|
| Name | Bubble Sort (a.k.a. Sinking Sort, Comparison Sort) |
| Inventor | Studied since 1956 (origin debated); name first used by Iverson, 1962 |
| Reference | Knuth TAOCP Vol. 3 §5.2.2 (Algorithm B); CLRS Problem 2-2 |
| Class | Comparison-based, in-place, stable |
| Best Time | O(n) — already-sorted input with early-exit optimization |
| Average Time | O(n²) |
| Worst Time | O(n²) |
| Auxiliary Space | O(1) |
| Adaptive | Yes (with early-exit) |
| Stable | Yes |
| Comparison Sort | Yes |
2. API / Interface Reference¶
Standard Function Signature¶
BUBBLE_SORT(A: array of T, cmp: (T, T) → int) → void
Sorts A in place using cmp, where cmp(a, b) returns:
< 0 if a should come before b
= 0 if a and b are equivalent
> 0 if a should come after b
Postcondition:
∀ i ∈ [0, n-2]: cmp(A[i], A[i+1]) ≤ 0
AND A is a permutation of the original input.
Language-Specific Equivalents¶
| Language | Built-in Sort | Notes |
|---|---|---|
| Go | sort.Slice, slices.Sort | Pdqsort, NOT Bubble Sort |
| Java | Arrays.sort, Collections.sort | Dual-Pivot Quicksort (primitives), TimSort (objects) |
| Python | sorted, list.sort | TimSort |
| C++ | std::sort, std::stable_sort | Introsort / TimSort |
| Rust | slice::sort | TimSort-like |
None of the standard libraries use Bubble Sort. Bubble Sort is taught, not deployed.
3. Core Rules and Behavioral Specification¶
Rule 1: Adjacent-Pair Compare-and-Swap¶
Knuth TAOCP §5.2.2: "On each pass, compare each adjacent pair and exchange if out of order."
# ✅ Correct — adjacent pair, in-place swap
if A[j] > A[j + 1]:
A[j], A[j + 1] = A[j + 1], A[j]
# ❌ Incorrect — non-adjacent comparison (not Bubble Sort)
if A[j] > A[k]: # k != j+1
A[j], A[k] = A[k], A[j]
Rule 2: Strict Greater-Than for Stability¶
Specification: Bubble Sort must use strict
>(not>=) to be stable.
# ✅ Correct — stable
if A[j] > A[j + 1]:
swap()
# ❌ Incorrect — destroys stability
if A[j] >= A[j + 1]:
swap()
Rule 3: Shrinking Inner Loop Bound¶
Optimization: After pass
i, the lasti+1elements are in final sorted positions. Inner loop must not revisit them.
# ✅ Correct
for j in range(0, n - 1 - i):
...
# ❌ Incorrect — wastes O(n) comparisons per pass (still correct, just slow)
for j in range(0, n - 1):
...
Rule 4: Early-Exit Flag¶
Optimization: If a complete pass performs no swaps, the array is sorted; terminate immediately.
# ✅ Correct — adaptive
swapped = False
for j in range(...):
if A[j] > A[j + 1]:
swap()
swapped = True
if not swapped:
break
Rule 5: Total Order Required¶
Precondition: The comparison function must define a total order — reflexive, antisymmetric, transitive, and total. Non-total orders cause undefined behavior.
| Property | Requirement |
|---|---|
| Reflexive | cmp(a, a) = 0 |
| Antisymmetric | cmp(a, b) < 0 ⇒ cmp(b, a) > 0 |
| Transitive | cmp(a, b) < 0 ∧ cmp(b, c) < 0 ⇒ cmp(a, c) < 0 |
| Total | ∀ a, b: cmp(a, b) is well-defined |
4. Schema / Parameters Reference¶
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
array | mutable sequence of comparable elements | ✅ | — | Input to be sorted in place |
cmp | function (a, b) → int | ❌ | natural order | Comparison function |
key | function T → K (where K comparable) | ❌ | identity | Extract sort key from each element |
reverse | boolean | ❌ | false | If true, sort descending |
Returns: void (in-place); some APIs return the array for chaining.
5. Behavioral Specification¶
Normal Operation¶
For input A of length n ≥ 2:
- Pass 1: Compare every adjacent pair from index 0 to n-2. Swap when out of order. The maximum reaches index n-1.
- Pass i (1 ≤ i ≤ n-2): Compare adjacent pairs from 0 to n-1-i. Swap when out of order. The i-th largest reaches index n-i.
- Termination: When pass i performs zero swaps (early-exit) OR i = n-1.
For inputs of length 0 or 1: return immediately, no work done.
Documented Limitations¶
| Limitation | Details | Workaround |
|---|---|---|
| O(n²) average case | Cannot beat Ω(n²) for adjacent-swap sorts | Use Insertion Sort (faster constant), Merge/Quick Sort (better Big-O) |
| Cache behavior | O(n²/B) cache misses on large n | Switch to cache-aware sort for n > L1/L2 size |
| No parallelism (sequential form) | Each pass depends on previous | Use Odd-Even Transposition variant for parallelism |
| Floating-point NaN | Undefined ordering with NaN | Filter NaN before sorting |
Error / Failure Conditions¶
| Error | Condition | Resolution |
|---|---|---|
IndexOutOfBoundsException / IndexError | Inner loop bound off by one (j < n instead of j < n-1) | Use j < n - 1 - i |
| Infinite loop | Comparator violates antisymmetry/transitivity | Verify comparator with property-based tests |
| Wrong order | Comparison operator reversed | Test with known input/output pairs |
NullPointerException (Java) | Sorting array with null elements | Use Comparator.nullsFirst(...) or filter |
| Sort instability when expected | >= used instead of > | Replace with strict > |
6. Edge Cases¶
| Edge Case | Specified Behavior | Rationale |
|---|---|---|
Empty array [] | Return immediately, no operations | Outer loop body executes 0 times |
Single element [x] | Return immediately | n-1 = 0 outer iterations |
All equal [5,5,5,5] | One pass, zero swaps, early exit (O(n)) | No element > next; flag never set |
Already sorted [1,2,3,4] | One pass, zero swaps, early exit (O(n)) | Best case |
Reverse sorted [5,4,3,2,1] | Maximum swaps n(n-1)/2 (O(n²)) | Worst case |
Two elements [2,1] | One swap, sorted | |
Duplicates [3,1,3,2,3] | Stable: original order of 3s preserved | Strict > only |
| NaN in input | Undefined behavior | Comparison with NaN returns false |
| Integer overflow in swap-by-arithmetic | Possible | Use temp variable, not arithmetic |
7. Complexity Compatibility Matrix¶
| Input Size | Time (Worst) | Time (Best) | Memory | Notes |
|---|---|---|---|---|
| n ≤ 10 | < 0.1 µs | < 0.1 µs | O(1) | Competitive on tiny n |
| n = 100 | ~10 µs | ~1 µs | O(1) | Still tolerable |
| n = 1,000 | ~1 ms | ~10 µs | O(1) | Noticeable |
| n = 10,000 | ~100 ms | ~100 µs | O(1) | Slow |
| n = 100,000 | ~10 s | ~1 ms | O(1) | Unacceptable |
| n = 1,000,000 | ~17 min | ~10 ms | O(1) | Catastrophic |
Comparator Compatibility¶
| Comparator Type | Compatible? | Notes |
|---|---|---|
| Total order on integers | ✅ | Standard |
| Total order on strings (lex) | ✅ | |
| Floating-point (IEEE 754, no NaN) | ✅ | |
| Floating-point with NaN | ⚠️ | Behavior undefined; filter first |
| Custom comparator (transitive) | ✅ | Must satisfy total order axioms |
| Hash-code-based comparator | ❌ | Not transitive in general |
| Random comparator | ❌ | Causes infinite loops or wrong results |
8. Reference Implementations¶
Knuth Algorithm B (TAOCP Vol. 3)¶
Algorithm B (Bubble sort).
Records R_1, R_2, ..., R_n with keys K_1, K_2, ..., K_n.
This algorithm rearranges them so K_1 ≤ K_2 ≤ ... ≤ K_n.
B1. [Initialize BOUND.] BOUND ← N.
B2. [Loop on j.] t ← 0.
For j = 1, 2, ..., BOUND - 1, do step B3:
B3. [Compare/exchange R_j : R_{j+1}.]
If K_j > K_{j+1}, then exchange R_j ↔ R_{j+1},
and set t ← j.
B4. [Any exchanges?] If t = 0, terminate.
Otherwise BOUND ← t and go back to B2.
This is the adaptive Bubble Sort (using last-swap-position to set the next bound) — slightly more efficient than the textbook "shrink by 1 per pass" variant.
Reference: Knuth's Adaptive Variant¶
Go¶
package main
import "fmt"
// BubbleSortKnuth implements Knuth's Algorithm B from TAOCP Vol. 3 §5.2.2.
func BubbleSortKnuth(arr []int) {
n := len(arr)
bound := n
for {
t := 0
for j := 0; j < bound-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
t = j + 1
}
}
if t == 0 {
return
}
bound = t
}
}
func main() {
data := []int{5, 1, 4, 2, 8}
BubbleSortKnuth(data)
fmt.Println(data)
}
Java¶
public class BubbleSortKnuth {
public static void sort(int[] arr) {
int n = arr.length;
int bound = n;
while (true) {
int t = 0;
for (int j = 0; j < bound - 1; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
t = j + 1;
}
}
if (t == 0) return;
bound = t;
}
}
}
Python¶
def bubble_sort_knuth(arr):
"""Knuth's Algorithm B from TAOCP Vol. 3 §5.2.2."""
n = len(arr)
bound = n
while True:
t = 0
for j in range(bound - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
t = j + 1
if t == 0:
return
bound = t
9. Compliance & Best Practices Checklist¶
- Uses strict
>comparison (preserves stability) - Implements early-exit flag (achieves O(n) on sorted input)
- Implements shrinking inner-loop bound (avoids redundant comparisons)
- Handles empty array and single element safely
- Comparator (if custom) defines a total order (transitive, antisymmetric)
- Documents in-place mutation in function comment/docstring
- Does not use Bubble Sort for n > 50 in production paths
- Includes regression test with adversarial input (reverse-sorted, all-equal, with duplicates)
- Uses language-native sort (
sort.Slice,Arrays.sort,sorted) for production
10. Related Documentation¶
| Topic | Section | Reference |
|---|---|---|
| Insertion Sort | ../03-insertion-sort/ | Faster O(n²) alternative |
| Merge Sort | ../02-merge-sort/ | O(n log n), stable |
| Quick Sort | ../04-quick-sort/ | O(n log n) average, in-place |
| Selection Sort | ../05-selection-sort/ | Minimum-write O(n²) sort |
| Heap Sort | ../06-heap-sort/ | O(n log n), in-place |
| Big-O Notation | ../../06-algorithmic-complexity/04-asymptotic-notation/01-big-o-notation/ | Complexity foundations |
| Sorting Networks | (External) | Knuth Vol. 3 §5.3.4 |
| Pdqsort (Go's sort) | (External) | Pdqsort paper, Peters 2021 |
| TimSort (Python/Java) | (External) | TimSort original spec |
Specification Notes: - The "canonical" Bubble Sort is the textbook 2-loop form (CLRS Problem 2-2), but Knuth's Algorithm B (using the last-swap-position) is strictly faster on most inputs and is the recommended reference. - Standard library sorts in every major language are NOT Bubble Sort — they are O(n log n) hybrids (TimSort, Pdqsort, Dual-Pivot Quicksort). - Bubble Sort's only legitimate production niche is detecting "is this array already sorted?" in a single O(n) pass — and even there, a direct linear scan is more idiomatic.