Divide
Split the input into smaller, similar subproblems.
Divide and conquer solves a problem by breaking it into smaller problems of the same kind, solving those subproblems, and combining their answers. Recursion is a natural way to express this pattern because the same function can solve each smaller instance.
Split the input into smaller, similar subproblems.
Solve each subproblem recursively. A base case stops the recursion.
Build the original answer from the smaller answers.
Every correct recursive algorithm needs a base case that can be answered directly and a recursive step that moves strictly closer to that base case. Otherwise the recursion never ends.
Suppose a staircase has \(n\) steps and we may climb either one or two steps at a time. Let \(W(n)\) be the number of different ways to reach the top. The final move came from step \(n-1\) or step \(n-2\), so the two disjoint sets of paths can be added.
The recurrence is Fibonacci-like. A direct recursive implementation mirrors the definition, but it recomputes the same values many times. Later, dynamic programming will show how to store those answers and reduce the runtime from exponential to linear.
Now start in the top-left cell of a board and move only right or down. To enter any other cell, the last move must come from above or from the left. Therefore the number of paths to a cell is the sum of those two neighboring values. Cells in the first row and first column have one path.
This example is recursive in structure even though the animation fills a table. The table makes overlapping subproblems visible: the same smaller path counts contribute to many later cells.
Merge sort divides an array into two halves until every subarray contains at most one item. A one-item array is already sorted. During the combine phase, two sorted arrays are merged by repeatedly taking the smaller front value.
def merge_sort(values):
if len(values) <= 1:
return values
middle = len(values) // 2
left = merge_sort(values[:middle])
right = merge_sort(values[middle:])
return merge(left, right)
Splitting produces two subproblems of half the input size. Merging examines all \(n\) values. This gives the recurrence \(T(n)=2T(n/2)+\Theta(n)\), whose solution is \(\Theta(n\log n)\).
The Master theorem solves many divide-and-conquer recurrences of the form below. Here \(a\) is the number of recursive subproblems, \(n/b\) is each subproblem's size, and \(\Theta(n^d)\) is the work performed outside the recursive calls.
The work near the root dominates.
Every level performs the same asymptotic work.
The many leaves dominate.
This is the common polynomial form of the theorem. To use it, identify \(a\), \(b\), and \(d\), calculate \(\log_b a\), compare it with \(d\), and select the matching case. More general versions require additional regularity conditions.
Use the animations above when you need a hint, then solve the short exercises below. New values can be generated repeatedly.
A rabbit can advance by one or two tiles. Which recurrence counts the ways it can reach tile \(n\)?
Count the recursive calls, determine the size of each subproblem, and then measure the work performed outside the recursive calls.