Section 5 · 8 min read
Divide and conquer
The divide-and-conquer pattern, its recurrences, and the standard algorithms: binary search, merge sort, quicksort, inversion counting, max-min, fast multiplication and fast exponentiation.
The pattern
Divide and conquer (D&C) has three steps:
- Divide the input into smaller instances of the same problem.
- Conquer each instance recursively; tiny instances are solved directly.
- Combine the sub-answers into the answer.
The running time is described by a recurrence of the form , where is the number of subproblems, their size, and the divide and combine costs. Unlike dynamic programming, the subproblems are independent, so nothing is gained by storing them. When a D&C recursion keeps solving the same subproblem (as in naive Fibonacci), it is a sign that DP is the right tool.
Reading the recurrence
The Master theorem compares with , the number of leaves of the recursion tree.
- Leaves dominate ( polynomially smaller): .
- Balanced (): .
- Root dominates ( polynomially larger, regular): .
Reducing the number of subproblems is often the key trick: Karatsuba goes from 4 to 3 half-size products, Strassen from 8 to 7.
Standard algorithms
| Algorithm | Recurrence | Time |
|---|---|---|
| Binary search | ||
| Merge sort | ||
| Quicksort, balanced split | ||
| Quicksort, worst split | ||
| Max and min | comparisons | |
| Maximum subarray | ||
| Counting inversions | ||
| Closest pair of points | ||
| Karatsuba multiplication | ||
| Naive block matrix multiply | ||
| Strassen | ||
| Fast exponentiation | multiplications | |
| Median of medians select |
Merge sort
merge_sort(A, lo, hi):
if lo >= hi: return
mid = (lo + hi) / 2
merge_sort(A, lo, mid)
merge_sort(A, mid + 1, hi)
merge(A, lo, mid, hi) // at most (hi - lo) comparisons
The recursion depth is , each level does merging work, so the total is regardless of input order. The worst-case comparison count satisfies with , which equals for powers of 2. The best case (each merge stops after one list runs out) is for powers of 2.
Quicksort
D&C with all the work in the divide step (partition) and no combine step. Its recurrence depends on the split. Any split that keeps a constant fraction on each side, even 1:99, gives ; a split of and at every level gives .
For example, if every partition puts exactly one quarter of the elements on one side, then . Each full level of the recursion tree costs . The shallowest leaf is at depth and the deepest at , so the total lies between and . Both are ; the split ratio changes only the constant. The quadratic worst case needs the subproblem to shrink by a constant amount, not a constant fraction.
Max and min together
maxmin(A, lo, hi):
if lo == hi: return (A[lo], A[lo]) // 0 comparisons
if hi == lo + 1: compare once, return (larger, smaller) // 1 comparison
mid = (lo + hi) / 2
(max1, min1) = maxmin(A, lo, mid)
(max2, min2) = maxmin(A, mid + 1, hi)
return (max(max1, max2), min(min1, min2)) // 2 comparisons
With and , unrolling for gives . This matches the lower bound for the problem.
Counting inversions
An inversion is a pair with . Modify merge sort: when merging, each time an element is taken from the right half while elements remain in the left half, add to the count (all of those are larger and earlier). Total instead of for checking every pair.
Maximum subarray
Split at the midpoint. The best subarray lies in the left half, in the right half, or crosses the middle. The crossing case takes the best suffix of the left half plus the best prefix of the right half, in linear time. Hence . Kadane's scan solves the same problem in and is not D&C.
Fast multiplication
Write and . Then , which needs four half-size products. Karatsuba notes that , so three products suffice. Strassen applies the same idea to block matrices: seven block products and 18 block additions.
Fast exponentiation
power(x, n):
if n == 0: return 1
h = power(x, n / 2) // one recursive call
if n is even: return h * h
else: return x * h * h
At most 2 multiplications per level and levels: . Calling power(x, n/2) twice instead of storing it in h changes the recurrence to .
Worked examples
Worked example 1: Worst-case merge sort comparisons
How many comparisons does merge sort make in the worst case on 8 elements?
, , , . Formula check: . For 16 elements: .
Worked example 2: D&C max-min count
How many comparisons does the recursive max-min algorithm make on ?
, , . Formula . Scanning twice would use .
Worked example 3: Inversions by merging
Count inversions in .
Split into and .
- Left half: (4,1) and (4,3), so 2 inversions; sorted .
- Right half: (9,7), (9,2), (7,2), so 3 inversions; sorted .
- Merge with : take 1; take 2 from the right while 3 and 4 remain on the left, add 2; then 3, 4, 7, 9 add nothing.
Total .
Worked example 4: Picking the faster recurrence
Algorithm P solves a problem by ; algorithm Q by . Which is faster?
P: dominates , so . Q: dominates , so . Q is asymptotically faster, though only slightly. For Q to beat P with subproblems of size , we need , that is .
Worked example 5: Karatsuba by hand
Multiply and with one level of Karatsuba in base 10, splitting each number into a high and a low digit.
, , , . Three products:
Middle term , which equals . Result . One multiplication was traded for a few additions; applied recursively, that trade is what lowers the exponent from 2 to . The base case matters in practice: for small numbers the extra additions cost more than they save, so real implementations switch to schoolbook multiplication below a threshold size.
Common traps
- D&C does not always beat brute force: is still .
- Two recursive calls on the same half (as in a careless power function) double the work.
- Quicksort's worst case is a property of the split, not of D&C in general.
- Merge sort makes the same number of recursive calls for every input; only comparisons change.
- When the combine step is , as in a naive closest-pair, .
Quick revision
- Write the recurrence first, then apply the Master theorem.
- Merge sort worst comparisons: .
- Max-min: comparisons.
- Karatsuba , Strassen .
- Fast power: multiplications with one recursive call.
- Inversions counted during merge in .