Merge sort & quicksort

Two divide-and-conquer sorts that reach O(n log n), the best any comparison-based sort can do. Merge sort does its work while combining; quicksort does it while splitting.

3 examples9 to practiceRead · 7 min
The key step

Two sorted lists merge into one in a single pass

Take two sorted piles of cards, face up. The smallest card overall is on top of one of the two piles, so compare the two top cards, take the smaller, and repeat. When one pile runs out, the rest of the other goes on the end. Every card is placed after one comparison at most: merging n items takes O(n).

Taking from the left pile when the two top cards are equal keeps equal items in their original order, which is what makes merge sort stable.

Merge sort

Merge sort splits until pieces are trivially sorted, then merges back up

Merge sort is divide and conquer with all the work in the combine step. Split the range in half, sort each half the same way, merge the two sorted halves. A range of one item is already sorted, which is the base case.

Every level of the recursion tree merges all n items once, and halving gives about log₂ n levels: O(n log n), on every input, sorted or shuffled. That is the T(n) = 2T(n ÷ 2) + O(n) row of Divide & conquer.

The cost is memory: merging needs a buffer to collect the merged items, O(n) extra space. In return merge sort is predictable, stable, and good at data that arrives as a stream or doesn't fit in memory, since merging only ever reads each run from front to back.

Quicksort

Quicksort puts one item in its final place, then sorts each side

Quicksort does its work while splitting. Pick one item, the pivot, and rearrange the range so every smaller item comes before it and every other item after it. That rearranging is the partition. The pivot is now exactly where it belongs in the sorted list, and the two sides can be sorted separately, with nothing to combine afterward.

The partition above keeps a boundary i: everything left of it is smaller than the pivot. Each item that is smaller gets swapped across the boundary, and the boundary moves one cell right. One pass, O(n), in place: no buffer needed.

Hard

Quicksort's partition, used to find one item

After a partition the pivot is exactly where sorting would put it. To find the item that belongs at one particular index, you only need the side that contains that index: recurse into one side and forget the other. On average each step halves the range, so the work is n + n/2 + n/4 + … ≈ 2n: O(n), cheaper than sorting. A bad pivot makes it O(n²), as in quicksort, so real code picks the pivot at random.

The catch

Quicksort is only as good as its pivots

A pivot near the middle value splits the range into two near-equal halves: about log₂ n levels, O(n log n). A pivot that is the smallest or largest item splits off nothing: the other side has n − 1 items, and repeated at every level that is n levels of O(n) work, O(n²).

The simple rule used above, "the last item is the pivot", hits that worst case on a list that is already sorted, which is a common input. Two standard fixes:

  • pick the pivot at random, so no particular input is reliably bad, and the expected time is O(n log n) on every input;
  • take the median of three items, such as the first, middle and last, which avoids the sorted-input trap.

The rule

Merge sort is O(n log n) on every input and stable, but needs O(n) extra memory. Quicksort sorts within the array, with no buffer beyond its O(log n) call stack, and is usually faster, but needs good pivots to avoid O(n²) and isn't stable.

The limit

No comparison sort can beat O(n log n)

Every sort so far is a comparison sort: it learns about the items only by comparing two of them. A list of n distinct items can arrive in any of n! orders, and the sort has to tell them all apart, since each needs different moves. Each comparison has two outcomes, so k comparisons can tell at most 2ᵏ cases apart. Telling n! cases apart needs at least log₂(n!) comparisons, and log₂(n!) grows like n log₂ n.

n

n! orders

log₂(n!) comparisons needed

n log₂ n

4

24

at least 5

8

8

40,320

at least 16

24

16

about 2.1 × 10¹³

at least 45

64

So merge sort is as good as comparison sorting gets, up to a constant. Beating O(n log n) means not comparing at all, which is what Linear-time sorting does for keys of a known, limited range.

In practice

Library sorts mix both ideas

Merge sort

Quicksort

Time

O(n log n) always

O(n log n) expected, O(n²) worst

Extra memory

O(n) buffer

O(log n) call stack, with care

Stable

Yes

No

Strength

Predictable; streams and huge data

Fast in memory; works in place

Real libraries combine them with insertion sort for small pieces. Python's sort and Java's sort for objects are merge-based and stable. Java's sort for primitive numbers is a quicksort variant with two pivots, where stability doesn't matter. C++'s std::sort is a quicksort that switches to a guaranteed O(n log n) method if the recursion gets too deep, so the O(n²) worst case can't happen.

Check yourself

4 questions

1. How long does merge sort take on a list that is already sorted?
2. Quicksort always picks the last item as the pivot. Which input makes it slowest?
3. Why can't any comparison sort guarantee fewer than about n log₂ n comparisons?
4. You must sort records by date while keeping records with the same date in their current order. Which is the safe choice?
Takeaways

Remember this

  • Merging two sorted runs takes one pass; merge sort splits to single items and merges back up in O(n log n) on every input.
  • Merge sort is stable and predictable, but needs O(n) extra memory.
  • Quicksort partitions around a pivot in place, then sorts each side; good pivots give O(n log n), bad ones O(n²).
  • Random pivots or the median of three protect quicksort from sorted and other unlucky inputs.
  • No comparison sort beats O(n log n): telling n! orders apart takes about n log₂ n comparisons.
Practice

Now solve some yourself

Only this topic 0 of 7 solved

  1. Easy
    Merge two sorted lists into the first one
    Merge sort & quicksort
  2. Easy
    Cheapest way to make every stick the same length
    Merge sort & quicksort
  3. Medium
    Arrange values small, big, small, big
    Merge sort & quicksort
  4. Medium
    Pairs of topics the teacher likes more
    Merge sort & quicksort
  5. Medium
    Arrange numbers into the largest number with your own comparison
    Merge sort & quicksort
  6. Medium
    The kth largest of numbers written as strings, by quickselect
    Merge sort & quicksort
  7. Hard
    Count pairs where the earlier is more than twice the later
    Merge sort & quicksort

Mixed with topics you have practiced 0 unlocked

    2 more once you have practiced Linked lists, Top-K & k-way merge. Play one of its examples to the end and solve one of its problems to unlock them.

    Problems open on their own sites (LeetCode, Codeforces, CodeChef, AtCoder, CSES) in a new tab. Tick Solved when you get it accepted there; your ticks decide which mixed problems open next.

    Next

    Linear-time sorting

    Continue