The 3-Question Recipe
Don’t trace every call while you write code. Trust the smaller call. People call this the leap of faith.
The recipe
Answer these before you write any code:
| # | Ask | Example: sum(n) = 1 + … + n |
|---|---|---|
| 1 | Promise: what does the method return? | sum(n) returns 1 + … + n |
| 2 | Base case: the smallest input with an obvious answer? | sum(0) = 0 |
| 3 | Smaller step: if a smaller call is right, how do I use it? | sum(n) = n + sum(n − 1) |
| ✓ | Check: does every call get closer to the base case? | n − 1 reaches 0 ✓ |
static int sum(int n) {
if (n == 0) return 0; // base case
return n + sum(n - 1); // trust sum(n - 1), add n
}
Why can you trust it? If it works for the smallest input, and each step works whenever the smaller call works, then it works for every input. (This is mathematical induction.)
Try it: sum of digits
Ways to make the input smaller
| Input | Smaller version | Example |
|---|---|---|
| number n | n − 1 | sum, factorial, stairs |
| number n | n / 10 (drop the last digit) | sum of digits |
| number n | n / 2 | fast power |
| array | index i + 1 (the rest) | count, search |
| array or string | both ends (lo + 1, hi − 1) | palindrome |
Choosing the base case
- Shrink the example until the answer is obvious (an empty array, n = 0).
- Find where the step breaks.
fib(n − 2)from n = 1 calls fib(−1), so both 0 and 1 need answers. - Check the guards first. Write
if (i == a.length)before you reada[i].
What should a base case return? It depends on the question:
| Question | Good end | Impossible path | Combine with |
|---|---|---|---|
| Count ways | 1 | 0 | + |
| Is it possible? | true | false | || |
| Minimum | 0 | (int) 1e9 | Math.min |
| Maximum | 0 | (int) -1e9 | Math.max |
Return the answer, don’t carry it
There are two ways to build an answer:
// Return style: build it on the way back up
int sum(int[] a, int i) {
if (i == a.length) return 0;
return a[i] + sum(a, i + 1);
}
// Carry style: pass the total down
int sum(int[] a, int i, int soFar) {
if (i == a.length) return soFar;
return sum(a, i + 1, soFar + a[i]);
}
Use return style for counts, min, max and yes/no. Memo (chapter 3) only works when the answer depends only on the parameters. Carry style is fine for printing all paths.
Java detail: int values are copied into each call. Arrays and lists are shared: every call sees the same list. So undo your change after the call, and save a copy with new ArrayList<>(path).
Your turn
Do Trace the Output, then this chapter’s problems in order. Before coding each one, write the three answers as comments.
Check yourself
sumTo(n) = 1 + 2 + … + n. Which line uses the recipe correctly?
fib(n) = fib(n − 1) + fib(n − 2). Which base case is enough?
You want the MINIMUM cost. A path is impossible. What should it return?
Item 3 of 44 · updated 2026-10-13