Bubble sort
Sort a list by walking through it again and again, swapping any two neighbors that are in the wrong order.
O(n²)Space O(1)2 practice problemsStep-through pictureThe idea
Bubble sort is the simplest sorting idea. Walk along the list and compare each item with the one right after it. If the left one is bigger, swap them. When you reach the end, the biggest item has been carried all the way to the last spot, like a bubble rising to the top of a glass.
Then do it again, but stop one spot earlier, because the last item is already in place. Each walk (called a pass) puts one more big item in its final spot at the end. After enough passes, the whole list is sorted.
Bubble sort is slow on big lists, so real code almost never uses it. It is worth learning because it shows the core moves of sorting, compare and swap, and because its number of swaps measures exactly how unsorted a list is.
An everyday example
Lining up kids by height, one pair at a time
A teacher lines up kids by height, but she may only look at two kids standing next to each other. She walks down the line. Whenever a taller kid stands just before a shorter one, she tells them to swap. By the end of her first walk, the tallest kid has been passed along to the very end. She walks the line again for the next tallest, and stops walking as soon as a full walk needs no swaps at all.
| In the example | In the code |
|---|---|
| One walk down the line | One pass of the outer loop |
| Looking at two kids side by side | items[i] > items[i + 1] |
| Telling them to swap places | [items[i], items[i + 1]] = [items[i + 1], items[i]] |
| The tallest kid reaching the end | items[end] is final after the pass |
| A walk with no swaps: done | if (!swapped) break |
From the slow way to bubble sort
- Start with the most basic question you can ask about a list: is it sorted? To answer it, you look at each pair of neighbors,
items[0]anditems[1], thenitems[1]anditems[2], and so on. If every pair is in order, the whole list is in order. That check costsn - 1comparisons. - Now turn the check into a fix. Whenever a pair is in the wrong order, swap the two items right away and keep walking. One walk does not sort the list, but it does something useful: the biggest item, once the walk reaches it, wins every comparison after that and gets carried all the way to the last index.
- So after one walk the last item is final, and the next walk can stop one step earlier. Keep a number
endfor the last index that may still be wrong, and lower it by one after each walk. Withnitems,n - 1walks are always enough, because each walk fixes one more item at the end. - One more fact saves a lot of work. If a whole walk makes no swap, every neighbor pair is in order, which is exactly the "is it sorted?" check from the first paragraph. So track a flag
swappedand stop as soon as a walk leaves itfalse. A walk over the unsorted part, a shrinkingend, and an early stop when nothing moved: that loop is bubble sort.
How it works
- Set
endto the last index. Everything afterendis already sorted. - Walk
ifrom0up toend - 1, comparingitems[i]withitems[i + 1]. - If
items[i]is bigger, swap the two and remember that a swap happened (swapped = true). - After the walk, the biggest item in
0..endsits atend. Moveendone step left. - If a whole pass made no swap, the list is sorted, so stop early.
Watch it run
This picture runs the code below on a real input. Press Step to move one step at a time, or Play to watch. The highlighted line is the one that just ran. Change the input to see other cases.
See every step at onceThe same run, laid out side by side
The code
Point at a note to light up the lines it explains.
function bubbleSort(items) {for (let end = items.length - 1; end > 0; end--) {let swapped = false;for (let i = 0; i < end; i++) {if (items[i] > items[i + 1]) {[items[i], items[i + 1]] = [items[i + 1], items[i]];swapped = true;}}if (!swapped) break;}return items;}
How fast it is
| Time | O(n²) |
|---|---|
| Extra memory | O(1) |
Up to n - 1 passes, each comparing up to n - 1 neighbor pairs. It only swaps items inside the array, so no extra memory grows with n.
- Best case
O(n)The list is already sorted, like
[2, 5, 8, 13]. The first pass makesn - 1comparisons and no swaps, andbreakstops the loop. - Average case
O(n²)A list in random order has about
n² / 4out-of-order pairs, and each swap fixes only one of them, so the work is still aboutn²divided by a small number. - Worst case
O(n²)A list in reverse order, like
[9, 7, 5, 3]. Every comparison swaps:n × (n - 1) / 2of each, which is 499,500 for 1,000 items.
Common mistakes
- Looping
iup toendinstead ofend - 1, soitems[i + 1]reads past the unsorted part, or past the array on the first pass. - Forgetting to shrink the range each pass. It still sorts, but every pass rechecks the sorted tail for nothing.
- Leaving out the
swappedcheck. Then even a sorted list costsO(n²). - Using
>=in the comparison. Equal items get swapped, which wastes work and breaks stability (equal items should keep their original order).
When to use it
Look for these clues in a problem:
- The task is to understand or count swaps of neighboring items, such as "how many adjacent swaps sort this list?".
- The list is tiny or already almost sorted, and the simplest possible code matters more than speed.
- The question asks for the state of the list after some number of passes.
Check yourself
Answer each one in your head first, then open it.
Why can the inner loop stop at end - 1 instead of the last index?
Every item after end is already one of the biggest items in its final place. Comparing them again can never cause a swap.
How many swaps does bubble sort make on [3, 1, 2]?
Two, because the list has two inversions: (3, 1) and (3, 2). Each swap fixes exactly one.
Why is the best case O(n) only with the swapped flag?
Without it, the loops always run every pass, about n² / 2 comparisons, even on a sorted list. The flag lets the first pass notice that nothing moved and stop.
What breaks if the comparison is >= instead of >?
Equal neighbors get swapped. The list still ends sorted, but equal items change order, so the sort is no longer stable, and it does extra swaps.
Practice problems
Stuck on one? Tuto is in the playground too, and gives you hints, never the answer.
0 of 2 solved. Solve one to earn the bronze medal, half for silver, and all of them for gold.
Start practicingGo further
You don't need these to start practicing. Open one when you want more.
Why it works
The reasoning that makes it always right
The loop keeps one rule, true at the top of every pass: the items after index end are the biggest items of the list, in sorted order, and they never move again. At the start, end is the last index, so the rule talks about an empty part and is true.
One pass keeps the rule. As i walks from 0 to end - 1, the biggest item seen so far always ends up at items[i + 1]: if it was at i, it is bigger than its neighbor and swaps right; if the neighbor is bigger, the neighbor is now the biggest seen. So when the walk ends, the biggest item of items[0..end] sits at end. Lowering end by one grows the sorted, final part by one item.
The loop ends because end drops by one each pass and stops at 0, so there are at most n - 1 passes. When it ends that way, the rule covers every index except 0, and the one item left must be the smallest. When it ends early, a full pass found every neighbor pair in order. A list where every neighbor pair is in order is sorted, so both endings give a sorted list.
Swaps only ever exchange two neighbors that are out of order. That fixes exactly one out-of-order pair (an inversion) and creates none, which is why the number of swaps equals the number of inversions, and why equal items never pass each other. The strict > comparison makes the sort stable.
Going deeper
3 more ideas
Why each pass fixes the biggest item
During a pass, the biggest item seen so far is always carried along: once the walk reaches it, it is bigger than its right neighbor, so it swaps right, then again, and again, until it reaches end. So after pass 1 the biggest item is last, after pass 2 the second biggest is second to last, and so on. After n - 1 passes every item is in place.
Swaps count the out-of-order pairs
A pair of positions i < j with items[i] > items[j] is called an inversion. Each swap of two neighbors fixes exactly one inversion and creates none. So bubble sort makes exactly as many swaps as the list has inversions. In [5, 1, 4, 2, 8] the inversions are (5, 1), (5, 4), (5, 2), and (4, 2): 4 of them, matching the 4 swaps.
How long it takes
In the worst case, a list in reverse order, pass 1 makes n - 1 comparisons, pass 2 makes n - 2, and so on, about n² / 2 in total. That is O(n²): 1,000 items need about 500,000 comparisons. With the early stop, a list that is already sorted needs just one pass, O(n).
Work it by hand
Every step in a table, with why
Sort 6 numbers. The list is sorted after the second pass, but the code needs a third pass with no swaps to know it.
items = [4, 1, 7, 3, 9, 8]
| Step | end | i | Pair | swapped | items after | What happens | Why |
|---|---|---|---|---|---|---|---|
| 1 | 5 | 0 | 4, 1 | true | [1, 4, 7, 3, 9, 8] | 4 > 1: swap | The first pass starts. 4 and 1 are in the wrong order, so they swap and swapped becomes true. |
| 2 | 5 | 1 | 4, 7 | true | [1, 4, 7, 3, 9, 8] | In order: keep | 4 is smaller than 7, so nothing moves. swapped stays true from the first swap. |
| 3 | 5 | 2 | 7, 3 | true | [1, 4, 3, 7, 9, 8] | 7 > 3: swap | 7 moves one step right. It will stop at the next pair, because 9 is bigger. |
| 4 | 5 | 3 | 7, 9 | true | [1, 4, 3, 7, 9, 8] | In order: keep | Now 9 is the biggest item seen so far, and it is the one that gets carried. |
| 5 | 5 | 4 | 9, 8 | true | [1, 4, 3, 7, 8, 9] | 9 > 8: swap | 9 reaches index 5. It is final, so the next pass stops one index earlier. |
| 6 | 4 | 0 | 1, 4 | false | [1, 4, 3, 7, 8, 9] | In order: keep | Pass 2 starts with end = 4 and swapped reset to false. |
| 7 | 4 | 1 | 4, 3 | true | [1, 3, 4, 7, 8, 9] | 4 > 3: swap | The last out-of-order pair is fixed. The list is now sorted, but the code can't know that yet. |
| 8 | 4 | 2 | 4, 7 | true | [1, 3, 4, 7, 8, 9] | In order: keep | Nothing to do. |
| 9 | 4 | 3 | 7, 8 | true | [1, 3, 4, 7, 8, 9] | In order: keep | Pass 2 ends with swapped = true, so another pass is needed. |
| 10 | 3 | 0 | 1, 3 | false | [1, 3, 4, 7, 8, 9] | In order: keep | Pass 3 starts with end = 3. |
| 11 | 3 | 1 | 3, 4 | false | [1, 3, 4, 7, 8, 9] | In order: keep | Still no swap. |
| 12 | 3 | 2 | 4, 7 | false | [1, 3, 4, 7, 8, 9] | No swap all pass: break | The whole pass made no swap, so if (!swapped) break ends the outer loop. |
It returns [1, 3, 4, 7, 8, 9] after 3 passes, 12 comparisons, and 4 swaps. The list had exactly 4 out-of-order pairs: (4, 1), (4, 3), (7, 3), and (9, 8). Without the early stop it would have made 15 comparisons.
Compared with other ways
When to pick something else
| Approach | Time | Extra memory | Choose it when |
|---|---|---|---|
| Bubble sort | O(n²) | O(1) | Teaching, tiny lists, or when you need the exact sequence of neighbor swaps, as in an animation. |
| Insertion sort | O(n²) | O(1) | Small or nearly sorted lists in real code. It does the same number of moves but with shifts, which are cheaper than swaps, and it fixes turtles in one step. |
| Merge sort | O(n log n) | O(n) | Large lists where you also need stability. It is far faster than bubble sort beyond a few dozen items. |
Built-in sort with a comparator | O(n log n) | O(n) | Almost always, in production JavaScript. It is stable and fast on partly sorted data. |
Extra memory: O(1): the sort only uses end, i, and swapped, and swaps items inside the array it was given. The returned array is the same array.
How the steps grow
- Bubble sort, Insertion sort
O(n²) - Merge sort, Built-in
sortwith a comparatorO(n log n)
| Approach | n = 10 | n = 1,000 | n = 1,000,000 |
|---|---|---|---|
| Bubble sort | 100 | 1 million | 1 trillion |
| Insertion sort | 100 | 1 million | 1 trillion |
| Merge sort | 33 | 9,966 | 20 million |
Built-in sort with a comparator | 33 | 9,966 | 20 million |
Inputs that need care
5 tricky inputs
- An empty list
[]or one item[6] endstarts at-1or0, soend > 0is false at once and the list is returned untouched.- All equal items:
[5, 5, 5, 5] 5 > 5is false for every pair, so the first pass makes no swap and the loop stops after 3 comparisons.- A small item at the far end:
[2, 3, 4, 5, 6, 1] - The
1moves left only one step per pass, so the code needs all 5 passes and 15 comparisons, even though only one item is out of place. Small items moving slowly like this are called turtles. - A big item at the front:
[6, 1, 2, 3, 4, 5] - The
6is carried to the end in the first pass. The second pass makes no swap and stops: 2 passes, 9 comparisons. Big items move fast, so they are called rabbits. - Equal items with labels, like two tasks both with priority
2 - The comparison is strict (
>), so equal items are never swapped and keep their original order. Changing it to>=would swap them and break stability.
Variations and follow-ups
Twists interviewers add
Cocktail shaker sort
Walk left to right, then right to left, then left to right again. The backward walk carries the smallest item to the front in one pass, so turtles like the 1 in [2, 3, 4, 5, 6, 1] are fixed in one backward walk instead of five forward ones. It is still O(n²) in the worst case.
Remember the last swap
Instead of lowering end by exactly one, set it to the index of the last swap in the pass. Everything after that swap was already in order, so the next pass can skip it. On lists with a long sorted tail this skips many comparisons.
Odd-even sort
Compare all pairs that start at even indexes, then all pairs that start at odd indexes, and repeat. The pairs in one round don't overlap, so they can be compared at the same time on parallel hardware. It needs at most n rounds.
Count the swaps instead of sorting
A frequent follow-up asks for the number of neighbor swaps needed to sort a list. That number equals the inversion count, which you can get in O(n log n) with merge sort instead of running bubble sort.
In an interview
Spot it, explain it, answer follow-ups
- You will rarely be asked to use bubble sort for speed. It shows up when a question talks about "adjacent swaps", "neighbors", or "the state after k passes". Recognize that the answer is about inversions, not about sorting fast.
- Explain it out loud as a rule: "after each pass, the biggest unsorted item is in its final place at
end". Then say why theswappedflag gives theO(n)best case. - Test by hand with an already sorted list (one pass, no swaps), a reversed list (every comparison swaps), and a list with a turtle like
[2, 3, 1], which shows why one pass is not enough. - The usual follow-up is "is it stable, and why?". Yes: it only swaps neighbors that are strictly out of order, so equal items never cross.
Where frontend code uses it
The same idea in real UI code
- Drag-to-reorder lists often move an item one slot at a time as you drag it past each neighbor, which is the same neighbor swap bubble sort makes.
- Sorting visualizers and teaching demos use bubble sort because every step is one easy-to-draw compare or swap.
- The number of bubble sort swaps equals the number of out-of-order pairs, a simple way to score how far a user's drag-and-drop ordering is from the correct one.