Memra

Merge sort & divide-and-conquer

◈ 4 cards

Divide / conquer / combine; the Θ(n) MERGE step; the recurrence T(n)=2T(n/2)+Θ(n) and its recursion-tree solution Θ(n lg n).

The divide-and-conquer template

Divide-and-conquer solves a problem by breaking it into smaller instances of the same problem:

  • Divide the instance into subproblems.
  • Conquer each subproblem recursively (or directly, if small enough — the base case).
  • Combine the subsolutions into a solution for the original.

Merge sort is the canonical example. To sort : divide at the midpoint , recursively sort and , then merge the two sorted halves. The recursion bottoms out at a one-element subarray (), which is trivially sorted. Almost all the work lives in the combine step — the opposite of quicksort, where the work is in the divide.

MERGE is the engine, and it is Θ(n)

MERGE(A, p, q, r) assumes and are each already sorted, and weaves them into one sorted run. It copies the two halves into temporaries and , then repeatedly appends the smaller of the two current front elements:

MERGE(L, R):
  i = j = 0; out = []
  while i < |L| and j < |R|
    if L[i] <= R[j]: out.append(L[i]); i = i + 1
    else:           out.append(R[j]); j = j + 1
  append the rest of L, then the rest of R
  return out

Why temporaries? Merging in place would overwrite values still needed for later comparisons. The cost: each of the elements is moved from into and then back into exactly once, so MERGE runs in — and that linear merge is precisely what makes the whole sort . Using <= (not <) in the comparison keeps merge sort stable.

The recurrence and its solution

Divide costs (a midpoint); conquer is ; combine is . So:

Recursion-tree argument. Picture the tree of subproblem costs. Level has nodes, each of size , so each node does merge work — and the level's total is , the same at every level. The doubling of nodes exactly cancels the halving of cost-per-node. The tree has levels (you can halve down to exactly times). So the total is

Unlike insertion sort, merge sort's running time is in all cases — it always splits, always recurses, always merges, regardless of the input order. The price is extra space for the temporaries.

A[p..q]A[q+1..r]5 2 4 7 1 3 2 6merge 8 → 8c5 2 4 7merge 4 → 4c5 22c4 72c1 3 2 6merge 4 → 4c1 32c2 62c
Doubling nodes × halving size = the same 8c of merge work at every level. Over lg n + 1 levels that product is the Θ(n lg n). (The final split to single elements is not drawn.)
01234567L2457iR1236jout1223sorted so farto fillL and R are copies because merging in place would overwrite values still needed. Each of the 8 elementsmoves out and back exactly once, so MERGE is Θ(n).
Merging the example’s two sorted halves. The next comparison is L[i] = 4 against R[j] = 6, so 4 is appended — one element per comparison.
NORMAL ~/memra/learn/comp-372/merge-sort-divide-and-conquer utf-8 LF