datarekha

Merge Sort & Divide-and-Conquer

Split the problem until it is trivial, then rebuild the answer — how merge sort reaches O(n log n) in every case and why that makes it the bedrock of serious sorting.

8 min read Intermediate Data Structures & Algorithms Lesson 9 of 32

What you'll learn

  • What divide and conquer means: split until trivial, solve each piece, then combine
  • Why merge sort is O(n log n) in the best, average, AND worst case — there is no bad input
  • What stability means and why merge sort has it, at the cost of O(n) extra space
  • Why predictable cost + stability make merge sort the basis of Timsort and big-data sorting

Before you start

Picture a deck of cards spread face-up on a table. If you could only ever compare two cards at a time, how would you put the whole deck in order?

Here is one neat way. Split the deck in half, sort each half, then merge the two sorted halves into one. But “sort each half” is the very same problem at half the size — so split again, and again, until every pile is a single card. A single card is already sorted. Now you walk back up: merge two one-card piles into a sorted pair, merge two sorted pairs into a sorted four, and keep merging until the whole deck is in order.

That is merge sort. And the move underneath it — break a problem into smaller copies of itself, solve those, and combine the results — is called divide and conquer, one of the most important patterns in all of computing.

The three steps, every time

Every divide-and-conquer algorithm wears the same skeleton:

  1. Divide — split the input into smaller sub-problems.
  2. Conquer — solve each one recursively, stopping at a base case small enough to answer directly.
  3. Combine — stitch the sub-answers into the answer for the whole.

For merge sort, those steps are: split the array at its midpoint; sort the two halves recursively; treat a 0- or 1-element array as already sorted (the base case); and merge two sorted halves by repeatedly taking the smaller of the two front elements.

dividemerge5 2 8 15 28 152812 51 81 2 5 8
Down (grey): split until every pile is one card. Up (green): merge sorted piles into bigger sorted piles.

The merge, step by step

Merging is where the real work lives, and it is simpler than it sounds. Given two already-sorted lists, you keep one finger on the front of each and always take the smaller of the two:

left  = [3, 27, 38]      right = [1, 9, 43]

3 vs 1   → take 1    →  [1]
3 vs 9   → take 3    →  [1, 3]
27 vs 9  → take 9    →  [1, 3, 9]
27 vs 43 → take 27   →  [1, 3, 9, 27]
38 vs 43 → take 38   →  [1, 3, 9, 27, 38]
right empty → copy 43 →  [1, 3, 9, 27, 38, 43]

Merging two halves of total size k costs at most k comparisons — one O(n) sweep. That sweep, repeated at every level, is the engine of the whole algorithm.

def merge(left, right):
    result = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:      # <= keeps equal elements in order (stable)
            result.append(left[i]); i += 1
        else:
            result.append(right[j]); j += 1
    result.extend(left[i:])          # copy whatever is left over
    result.extend(right[j:])
    return result

def merge_sort(arr):
    if len(arr) <= 1:
        return arr                   # base case: already sorted
    mid = len(arr) // 2
    return merge(merge_sort(arr[:mid]), merge_sort(arr[mid:]))

print(merge_sort([38, 27, 43, 3, 9, 82, 10, 1]))
[1, 3, 9, 10, 27, 38, 43, 82]

Why it is O(n log n) — always

PropertyValue
Best / average / worstO(n log n)
Extra spaceO(n) for the merge buffer
StableYes
In-placeNo

That O(n) extra space is the price of the guarantee. In-place would mean sorting with only a constant scrap of memory beyond the input; merge sort is not in-place, because the merge has to write its output into a temporary buffer — you cannot fold two sorted halves together inside the original array without losing track of what goes where.

Why stability is worth caring about

A sort is stable if elements that compare as equal come out in the same order they went in. Merge sort is stable, because the merge takes the left element first whenever two are equal (that is the <= in the code).

Why does it matter? Suppose you sort a table of students by name, then re-sort by grade. With a stable sort, students sharing a grade stay in the name order you just gave them. With an unstable sort, that earlier work is scrambled. Stability is what lets you build a multi-key order one key at a time — the subject of the Timsort lesson.

Practice

Quick check

0/3
Q1What is the time complexity of merge sort in the worst case?
Q2Why does merge sort need O(n) extra space?
Q3You sort a list of (name, score) tuples by score with a stable merge sort. Will names with equal scores keep their original order?

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Practice this in an interview

All questions

Related lessons

Explore further

Glossary terms
Skip to content