Sorting algorithm visualizer

Bubble, insertion, selection, merge and quicksort, racing side by side.

Step 0 of 0

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.