Mergesort is a divide-and-conquer comparison sort that recursively splits the array in half, sorts each half, then merges them. It guarantees O(n log n) on every input, and is the basis for Java's Arrays.sort on objects.
Below, a full split-and-merge trace, the two-pointer merge in detail, an iterative implementation, and guidance on when mergesort is the right choice.
| Best | Average | Worst | Space (Worst) |
|---|---|---|---|
Ω(n log n) |
Θ(n log n) |
O(n log n) |
O(n) |
Split the array at the midpoint. Recursively sort each half. Merge the two sorted halves into one by walking both with two pointers, repeatedly taking the smaller head. The merge step is the workhorse — every element is compared and copied O(log n) times, giving the n log n total.
def mergesort(arr):
"""Top-down recursive mergesort."""
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = mergesort(arr[:mid])
right = mergesort(arr[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]:
out.append(left[i]); i += 1
else:
out.append(right[j]); j += 1
out.extend(left[i:])
out.extend(right[j:])
return out
print(mergesort([3, 6, 1, 8, 2, 9, 4])) # [1, 2, 3, 4, 6, 8, 9]
The top half of the diagram is the split phase: the array is cut at its midpoint until every piece has length one, which is trivially sorted. With seven elements the first split gives [3, 6, 1] and [8, 2, 9, 4]. The recursion depth is ⌈log2 n⌉, here three levels. The odd length is handled naturally: the left half simply gets one element fewer than the right. The green rows are the merge phase, read bottom-up: pairs of single elements merge into sorted pairs, sorted pairs merge into runs of three and four, and the final merge produces the sorted array. Each level of the tree touches every element once, and there are about log2 n levels, which is where n log n comes from.
Merging two sorted lists of total length n takes at most n−1 comparisons. Two indices start at the heads of the runs; at each step the smaller head is appended to the output and its index advances. When one run is exhausted, the remainder of the other is copied without comparisons. Using <= rather than < when the heads are equal takes from the left run first, which preserves the original order of equal keys and makes the sort stable. Changing that single operator silently breaks stability.
def mergesort_bottom_up(a):
a = list(a)
n, width = len(a), 1
buf = [None] * n
while width < n:
for lo in range(0, n, 2 * width):
mid, hi = min(lo + width, n), min(lo + 2 * width, n)
i, j, k = lo, mid, lo
while i < mid and j < hi:
if a[i] <= a[j]:
buf[k] = a[i]; i += 1
else:
buf[k] = a[j]; j += 1
k += 1
buf[k:hi] = a[i:mid] + a[j:hi]
a, buf = buf, a
width *= 2
return a
print(mergesort_bottom_up([3, 6, 1, 8, 2, 9, 4])) # [1, 2, 3, 4, 6, 8, 9]
The bottom-up form avoids recursion: it merges runs of width 1, then 2, then 4, swapping the roles of the array and a single reusable buffer each round. It allocates the O(n) buffer once instead of creating new lists at every recursive call, as the top-down version above does with slicing. This is the shape used for external sorting, where runs are files on disk and each pass streams them sequentially.
Prefer quicksort or introsort for in-memory arrays of primitives where stability does not matter and memory is tight; their in-place partitioning and cache behaviour usually make them faster. In Python, call sorted(), which is Timsort, a mergesort descendant. Java uses a Timsort variant for sorting object arrays for the same reason: objects often carry secondary fields, so callers expect equal keys to stay in their original order, and real data tends to contain partially sorted stretches that a run-aware mergesort exploits. For primitive arrays, where equal values are indistinguishable and stability is meaningless, Java uses a dual-pivot quicksort instead./p>
< instead of <= in the merge breaks stability.See Timsort for the adaptive, run-detecting descendant, Quicksort and Heapsort for in-place alternatives, and Insertion Sort for the small-array base case.