Merge Sort is the divide-and-conquer workhorse: split a list in half, sort each half, then merge the two sorted halves back together. Because the halves are already sorted, the merge is a fast two-pointer walk.
The guarantee: O(n log n) on every input — no unlucky pivots, no quadratic worst case — at the price of O(n) extra memory for merge buffers.
How It Works
- Divide: find the midpoint of the array and recursively split it into left and right halves.
- Conquer: keep splitting until each sub-array contains one element (the base case — a single element is trivially sorted).
- Combine: merge two sorted sub-arrays by comparing their smallest remaining elements and appending the smaller into a target array.
- Repeat the merge up the recursion tree until the entire array is unified.
Key Insight
The merge step does all the sorting — splitting only creates the sorted pieces. Merging two sorted lists of total size k costs O(k), and the recursion produces log n levels, each touching all n elements.
That balance — n elements per level × log n levels — is exactly O(n log n). Merge Sort is also stable, and the natural choice for sequentially-accessed data (linked lists, tape drives, external files).
Worked Example
Sort [5, 3, 8, 1, 2] with Merge Sort:
- Split:
[5, 3, 8, 1, 2]→[5, 3]+[8, 1, 2] - Split again:
[5, 3]→[5]+[3];[8, 1, 2]→[8]+[1, 2]→[1]+[2] - Merge
[5]+[3]→[3, 5] - Merge
[1]+[2]→[1, 2]; then[8]+[1, 2]→[1, 2, 8] - Merge
[3, 5]+[1, 2, 8]→[1, 2, 3, 5, 8]
Result: [1, 2, 3, 5, 8]. In the visualizer, the two-pointer merge walks each pair of sorted runs — watch the smaller of the two “heads” get written to the output each step.
Edge Cases & Pitfalls
- Single element or empty — trivially sorted: the recursion base case.
- Duplicates — the merge uses
<=, so equal elements from the left run are written first, preserving stability. - Large arrays — needs
O(n)auxiliary space. If memory is tight, consider an in-place merge variant or Heapsort. - Stack depth — recursive splitting uses
O(log n)call stack. Deep recursion on huge inputs can overflow; an iterative (bottom-up) version avoids this.
Comparison With Other Sorts
| Scenario | Merge Sort | Quick Sort | Heap Sort |
|---|---|---|---|
| Worst case | O(n log n) guaranteed | O(n²) on bad pivots | O(n log n) |
| Stability | Stable | Unstable | Unstable |
| Space | O(n) auxiliary | O(log n) stack | O(1) |
| Best when | Stability + guaranteed bound | In-memory speed | O(1) space guarantee |
Applications
- Sorting linked lists (merge is sequential-access friendly; no random indexing needed)
- External sorting of files too large for memory (chunk → sort → merge)
- Library
sortimplementations where stability is required (e.g.,Collections.sortin Java for objects) - Real-time systems needing a predictable worst case
Practice Trajectory
- Hand-split and re-merge
[6, 1, 4, 2, 9, 3], drawing the full recursion tree. - Implement
merge(left, right)with two pointers and confirm it is linear. - Trace the merge of
[1, 4, 7]and[2, 5, 8]step by step. - Explain why the total work is
n log neven when the input is already sorted. - Implement an iterative (bottom-up) version that avoids recursion.