What is bubble sort?
Bubble sort walks the array comparing each pair of neighbors, swapping them whenever the left one is bigger than the right one. Do that walk enough times and the biggest values keep getting nudged one step at a time toward the end, the way the biggest bubbles in a fizzy drink rise to the top fastest.
- Pass
- One full walk through the array, comparing every adjacent pair once
- Adjacent swap
- Swapping two neighboring elements because the left one is bigger than the right one
- Sorted tail
- The end portion that's already in its final position and doesn't need checking again
Why bubble sort at all?
Picture bubbles rising through a glass of soda. The biggest ones push past their smaller neighbors fastest, because at every point where a big bubble sits just below a smaller one, they swap places.
Bubble sort works the same way: compare two neighbors, swap them if they're in the wrong order, and move on to the next pair. By the end of one pass, the largest unsorted value has "risen" all the way to its correct spot at the end.
Each swap nudges the bigger value one step closer to the end.
What does each pass actually do?
Every pass is the same four-part rhythm: compare a pair, swap if needed, keep walking, and remember what's already settled so you never recheck it.
Compare, then swap if needed
Look at arr[i] and arr[i+1]. If arr[i] is bigger, swap them. If not,
leave them alone and move the comparison one step to the right.
3 > 1, so they swap. 3 < 2 next, so they swap again.
A full pass bubbles the max to the end
Repeat the compare-and-swap step across the whole array, left to right. Whatever value keeps winning its comparisons keeps moving right — by the end of the pass, the largest value in the unsorted portion sits exactly where it belongs.
6 wins every comparison it's in, so it ends the pass at index 4.
The sorted tail only grows
Each pass locks in one more correct value at the end. The next pass never needs to look at that locked region again, so it only compares the shrinking unsorted prefix.
The locked region (dimmed) never gets compared again.
No swaps means you're done
Track whether any swap happened during a pass. If a whole pass goes by with zero swaps, every element is already in order — stop immediately instead of running the remaining passes.
Zero swaps this pass — the array was already sorted, so bubble sort stops early.
Two versions
The basic version always runs n - 1 passes, whether or not it needs to. The optimized version
adds one flag and stops the moment a pass makes no swaps.
Basic pass
Two nested loops: the outer loop counts down the unsorted region, the inner loop walks it comparing and swapping neighbors. It never checks whether it finished early — it just runs every pass.
Pass 3 runs even though pass 2 already finished the job.
function bubbleSort(arr: number[]): number[] {
const n = arr.length;
for (let pass = 0; pass < n - 1; pass++) {
for (let i = 0; i < n - 1 - pass; i++) {
if (arr[i] > arr[i + 1]) {
[arr[i], arr[i + 1]] = [arr[i + 1], arr[i]]; // swap
}
}
// no check here — every pass runs, even a wasted one
}
return arr;
}
Early-exit optimized
Same nested loops, plus one swapped flag reset to false at the start of every pass. If it's
still false after the inner loop finishes, nothing moved — the array is sorted, so break out early.
Pass 2 finds zero swaps, so passes 3 and 4 never run.
function bubbleSort(arr: number[]): number[] {
const n = arr.length;
for (let pass = 0; pass < n - 1; pass++) {
let swapped = false;
for (let i = 0; i < n - 1 - pass; i++) {
if (arr[i] > arr[i + 1]) {
[arr[i], arr[i + 1]] = [arr[i + 1], arr[i]]; // swap
swapped = true;
}
}
if (!swapped) break; // nothing moved this pass — already sorted
}
return arr;
}
Where it works — and where it breaks
Bubble sort's whole trade is simplicity: it's easy to write correctly, uses no extra memory, and handles small or nearly-sorted input just fine. The cost is what happens as the input grows.
Works well on small or nearly-sorted input
When only a few pairs are out of order, each pass finds almost nothing to swap and the early-exit
version stops after a couple of passes — close to O(n) in practice.
Breaks down on large or reverse-sorted input
A reverse-sorted array forces a swap on every single comparison, every pass — O(n²) comparisons
and swaps. Double the input and the work roughly quadruples.