Sorting algorithm visualizer
Bubble, insertion, selection, merge and quicksort, racing side by side.
Bubble sort
Done in 0 steps- Best
- O(n)
- Average
- O(n²)
- Worst
- O(n²)
- Space
- O(1)
- Stable
- Yes
Repeatedly swaps adjacent pairs. Only useful as a teaching example — and as the best case for nearly-sorted data.
Insertion sort
Done in 0 steps- Best
- O(n)
- Average
- O(n²)
- Worst
- O(n²)
- Space
- O(1)
- Stable
- Yes
How most people sort a hand of cards. Genuinely fast for small or nearly-sorted arrays, which is why real sorts fall back to it.
Selection sort
Done in 0 steps- Best
- O(n²)
- Average
- O(n²)
- Worst
- O(n²)
- Space
- O(1)
- Stable
- No
Finds the minimum and puts it in place. Always n²/2 comparisons but at most n swaps — the choice when writing is far more expensive than reading.
Merge sort
Done in 0 steps- Best
- O(n log n)
- Average
- O(n log n)
- Worst
- O(n log n)
- Space
- O(n)
- Stable
- Yes
Divide, sort each half, merge. Guaranteed n log n and stable, at the cost of extra memory. The basis of most library sorts.
Quicksort
Done in 0 steps- Best
- O(n log n)
- Average
- O(n log n)
- Worst
- O(n²)
- Space
- O(log n)
- Stable
- No
Partition around a pivot, recurse. Fastest in practice thanks to cache behaviour, but the worst case is quadratic on adversarial input.
How it works
Colours mark what the algorithm is doing: amber for a comparison, red for a move, green for elements in their final position.
Try “already sorted”
Bubble and insertion sort finish almost immediately — they detect that nothing needs moving and stop. Selection sort takes exactly as long as always, because it scans for the minimum regardless. Quicksort with a last-element pivot hits its worst case here, degrading to O(n²), which is precisely why real implementations choose the pivot more carefully.
What O(n log n) buys you
At 40 elements the quadratic sorts are merely slower. At 10,000 the difference is roughly 100 million operations against 130,000 — the difference between a stalled interface and an instant one. Asymptotic complexity matters exactly when the data grows.
Stability
A stable sort preserves the relative order of equal elements. Sort a table by name, then by
department: with a stable sort the names remain alphabetical within each department. With an
unstable sort they scramble. This is why JavaScript's Array.sort has been required
to be stable since ES2019.
What is actually used
Nobody ships a textbook algorithm. Modern engines use hybrids: Timsort (Python,
Java for objects, and most JavaScript engines) is merge sort that detects already-sorted runs
and switches to insertion sort on small pieces. Introsort (C++ std::sort) runs quicksort but switches to heapsort when the recursion goes too
deep, capping the worst case at n log n. Both exploit the same insight visible above: insertion
sort is genuinely the fastest option on small or nearly-ordered data.
The n log n lower bound
No comparison-based sort can beat O(n log n) in the worst case. There are n! possible orderings and each comparison gives one bit, so you need at least log₂(n!) ≈ n log n comparisons to distinguish them. Faster sorts exist — counting sort, radix sort — but they work by not comparing at all, which requires knowing something about the values in advance.