Mergesort
Overviewโ
Mergesort splits the array in half, sorts each half recursively, and merges the two sorted halves back together. The insight is that merging two already-sorted sequences is linear โ you compare their two front elements and take the smaller, repeatedly.
Splitting costs nothing and produces log n levels; merging costs O(n) per level. Hence O(n log n), on every input, with no worst case to worry about.

Core Conceptsโ
| Property | Value |
|---|---|
| Best case | O(n log n) |
| Average | O(n log n) |
| Worst case | O(n log n) โ guaranteed |
| Space | O(n) โ the merge buffer |
| Stable | Yes |
| Adaptive | No, in the classic form (but see Timsort) |
| Parallelises | Well โ the two halves are independent |
Architecture / Mechanismโ
def merge_sort(a):
if len(a) <= 1:
return a
mid = len(a) // 2
left = merge_sort(a[:mid]) # sort each half
right = merge_sort(a[mid:])
return merge(left, right)
def merge(left, right):
out, i, j = [], 0, 0
while i < len(left) and j < len(right):
if left[i] <= right[j]: # <= keeps the sort stable
out.append(left[i]); i += 1
else:
out.append(right[j]); j += 1
out.extend(left[i:]) # one side is exhausted; append the rest
out.extend(right[j:])
return out

Why the complexity is exactly O(n log n)โ
The recursion halves the input, so it has logโ n levels. Every level merges a total of n elements,
regardless of how they are distributed across subarrays. Work per level is therefore ฮ(n), and the
total is ฮ(n log n) โ with no dependence on the data, which is why best, average and worst are all
the same.
Stability comes from one characterโ
left[i] <= right[j] takes from the left run when elements compare equal. Since the left run
holds elements that came earlier in the original array, equal elements keep their original order.
Change it to < and the merge takes from the right on ties, silently destroying stability.
Practical Usageโ
Mergesort is the right choice when:
- Stability is required โ sorting by a secondary key after a primary one.
- Worst-case guarantees matter โ real-time or adversarial contexts where quicksort's O(nยฒ) is unacceptable.
- The data does not fit in memory. External mergesort reads sorted runs from disk and merges them with sequential I/O, which is the one access pattern storage is good at. This is how databases sort tables larger than RAM.
- You are sorting a linked list. Merging lists needs only pointer rewiring โ O(1) extra space, no random access required. This is the one case where mergesort is better on a list than on an array.
- You want to parallelise. The two recursive calls share nothing.
# Bottom-up mergesort โ no recursion, same complexity
def merge_sort_iterative(a):
width = 1
while width < len(a):
for i in range(0, len(a), 2 * width):
a[i:i + 2 * width] = merge(a[i:i + width], a[i + width:i + 2 * width])
width *= 2
return a
Edge Cases & Pitfallsโ
Mergesort cannot merge in place efficiently. Naive in-place merge algorithms exist but are either O(nยฒ) or have constant factors bad enough to erase the benefit. For a large array this means a second buffer the same size โ which can be the deciding factor on memory-constrained systems, and is the main reason quicksort is preferred for in-memory array sorting.
A good implementation allocates one scratch buffer up front and reuses it, rather than allocating per merge as the readable version above does.
- Slicing allocates. The Python above creates new lists at every level โ clear, but it does roughly O(n log n) allocation. Production code passes indices into a single shared buffer.
<instead of<=in the merge silently loses stability.- Recursion depth is O(log n), which is safe โ unlike quicksort's worst case.
Comparisonsโ
| Mergesort | Quicksort | Heapsort | |
|---|---|---|---|
| Worst case | O(n log n) | O(nยฒ) | O(n log n) |
| Space | O(n) | O(log n) | O(1) |
| Stable | Yes | No | No |
| Locality | Good โ sequential merges | Excellent | Poor โ jumps around |
| Typical speed on arrays | Good | Fastest | Slowest of the three |
| Linked lists | Ideal | Awkward | Impractical |
Referencesโ
- Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms, ยง2.3 โ mergesort, and the recurrence-tree analysis of its complexity.
- Knuth, The Art of Computer Programming, Vol. 3, ยง5.2.4 โ merging and external sorting, including multiway merges.
Books & Videosโ
- VisuAlgo โ Sorting โ watch the merge levels build up.
Related Pagesโ
- Quicksort โ the in-place alternative with a worse worst case.
- Divide & Conquer โ the general pattern this instantiates.
- Choosing a Sort โ Timsort, which is an adaptive mergesort.