JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

Divide, Conquer, Combine: The Template

The three-line recipe behind merge sort, binary search, and fast multiplication — and the recurrence that turns the recipe into a running time.

One idea, three moves

In the brute-force rung you met algorithms that face a problem head-on: list every possibility and check it. Divide and conquer takes the opposite stance. Instead of attacking an input of size n directly, it cuts the problem into smaller pieces of the same kind, solves those, and stitches the answers back together. The whole divide / conquer / combine template is just three moves: divide the input into a few subproblems, conquer each subproblem by recursion, and combine their solutions into a solution for the original. Almost every fast algorithm you are about to meet — sorting, searching, multiplying big numbers — is this same skeleton wearing different clothes.

The "conquer" step is where the magic hides, because it is recursive: to solve a subproblem of size n/2 we apply the very same three moves to it, which split it again, and again, until the pieces are so small the answer is obvious. That smallest case where we stop and answer directly — a list of one element is already sorted, a single number is its own product — is the base case. Without it the recursion would fall forever; with it, the splitting bottoms out and the answers begin flowing back up.

Written as a recipe, a solve routine has the same five lines every time: if the input is small enough, return the answer directly (the base case); otherwise split it into subproblems P1, P2, ... (divide), recursively call solve on each to get S1, S2, ... (conquer), and return combine(S1, S2, ...) (combine). Memorize that skeleton, because every algorithm in this rung just fills in three blanks — how to split, what the base case is, and how to combine — while the recursive plumbing stays identical.

Why the pieces must not overlap

There is a quiet assumption that makes the whole scheme honest, and it is worth naming. For divide and conquer to pay off, the subproblems should be independent — each piece solvable on its own, with no shared work between them. When you sort the left half of an array, that work tells you nothing about, and is not reused by, sorting the right half. This independence is exactly what lets us add up costs cleanly: the total conquer cost is just the cost of the left call plus the cost of the right call, with no overlap to double-count.

Notice the division of labour between the three moves. The divide step is usually cheap — often just picking a midpoint. The conquer step does no real work itself; it merely delegates to smaller copies of the problem. So the cleverness, and usually the cost, lives in the combine step: how much effort it takes to fuse two solved halves into one solved whole. A surprising amount of algorithm design comes down to a single question — can I make the combine cheaper? — and the guides ahead are largely variations on answering it.

From the recipe to a running time

How fast is a divide-and-conquer algorithm? We cannot just count loops, because the work is scattered across a tree of recursive calls. Instead we write down a recurrence: an equation that defines the cost on size n in terms of the cost on the smaller pieces. If we split into a subproblems, each of size n/b, and the divide-plus-combine work outside the recursion costs f(n), the cost obeys T(n) = a T(n/b) + f(n). This one line captures the entire algorithm's shape: a counts the branches, b says how much smaller each piece is, and f(n) is the price of dividing and combining at this level.

T(n) = a * T(n/b) + f(n)        # a branches, each of size n/b, plus f(n) outside

  merge sort:   T(n) = 2 T(n/2) + O(n)      # split in 2, merge costs O(n)
  binary search: T(n) = 1 T(n/2) + O(1)     # one half survives, O(1) to pick it
The general divide-and-conquer recurrence, and two famous instances of it.

To turn that recurrence into a clean bound like O(n log n), the most intuitive tool is the recursion tree. Draw the call at the top as a single node costing f(n); below it draw its a children, each costing f(n/b); below those, their children; and so on down to the base cases. Now add the costs level by level. The running time is the sum over all levels, and the shape of that sum — whether it is dominated by the top, spread evenly, or piled at the leaves — tells you the answer. It is a picture you can literally read the running time off of.

Reading a recursion tree: merge sort

Let us make the tree concrete with the recurrence T(n) = 2 T(n/2) + O(n), the signature of merge sort. The top node costs about n (the work to merge two sorted halves of total length n). It has two children of size n/2, each costing about n/2 — together n again. Those four grandchildren of size n/4 each cost about n/4 — together n once more. Every full level sums to the same n, no matter how deep we go. That is the heart of why this recurrence gives n log n.

  1. Each level of the tree sums to about n total work, because halving the size doubles the number of nodes — the two effects cancel.
  2. Starting from n and halving each level, it takes about log n levels to shrink down to the size-1 base cases.
  3. Multiply: about n work per level times about log n levels gives a total of about n log n.
  4. So T(n) = 2 T(n/2) + O(n) resolves to Theta(n log n) — the famous cost of sorting by divide and conquer.

Contrast that with binary search, whose recurrence is T(n) = T(n/2) + O(1). Here we make only one recursive call, not two — after comparing with the middle element we throw away an entire half and recurse on the survivor. The tree is not bushy but a single thin path, with O(1) work per level and about log n levels, giving O(log n) overall. Same template, but because we discard half the input instead of recursing into both halves, the cost collapses from n log n all the way down to log n. The number of branches you keep is everything.

The shortcut, and where it stops

Drawing a tree every time gets tedious, so there is a shortcut: the master theorem. For recurrences of the exact form T(n) = a T(n/b) + f(n), it reads off the answer by comparing two growth rates — the work f(n) you do per level against a benchmark n^(log base b of a) set by how fast the branches multiply. Whichever grows faster wins and determines the total; when they tie, an extra factor of log n appears (that tie is the merge-sort case, T(n) = 2 T(n/2) + O(n), and it is exactly why sorting lands at Theta(n log n)). Later guides in the recurrences rung make this precise; for now, treat it as a fast oracle for the trees you have been drawing by hand.

Two honest caveats close the loop. First, getting the recurrence right does not prove the algorithm correct — it only tells you the cost. Correctness still rides on the combine step actually fusing two correct subsolutions into a correct whole, which you prove by induction on the recursive calls: assume the smaller calls return right answers, then show combine preserves rightness. Second, all of this is asymptotic: a Theta(n log n) divide-and-conquer method can still lose to a simple quadratic one on tiny inputs, because the recursion's bookkeeping carries a real per-call overhead the hidden constant quietly absorbs. That is why production sort routines switch to insertion sort once the pieces get small.