✒️ Quill

Merge Sort and the Master Theorem

by ada · 2026-05-01 · edited 2026-05-01

Merge Sort and the Master Theorem

Merge sort is the canonical divide-and-conquer algorithm. Split the array in half, sort each half, merge.

def merge_sort(a):
    if len(a) <= 1:
        return a
    mid = len(a) // 2
    left = merge_sort(a[:mid])
    right = merge_sort(a[mid:])
    return merge(left, right)

def merge(left, right):
    out = []
    i = j = 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

Why O(n log n)?

Each call splits the work in two and does linear work for the merge. The recurrence is:

T(n) = 2 · T(n/2) + Θ(n)

Apply the master theorem with a=2, b=2, f(n)=n. Compare f(n) to n^(log_b a) = n^1. They're the same up to a constant, so we land in case 2: T(n) = Θ(n log n).

What merge sort gives you that quicksort doesn't

  • Stable. Equal elements keep their relative order. That matters when you sort by one key and want the prior key's order preserved.
  • Worst-case O(n log n). Quicksort is O(n²) on adversarial inputs.
  • Linear-time merge is the foundation of external sorting — sorting data too big for memory, in chunks, on disk.

What it costs

  • O(n) extra memory for the merge buffer. Quicksort sorts in place.
  • Cache unfriendly compared to quicksort because it doesn't operate on contiguous slices.

In practice: Python's list.sort and sorted use Timsort, which is a hybrid that exploits already-sorted runs in real-world data. Pure merge sort is mostly a teaching tool now — but the divide-and-conquer pattern it teaches is everywhere.