Chapter 5: Recursion and Divide-and-Conquer Fundamentals
5.02Beginner7 topics28 min6 interactive animations

Classical examples

Seven classic problems show the shapes recursion takes: counting down with one call, branching into two, shrinking numbers, and halving. Each has an animation of its calls, so you can see exactly how the answer is assembled.

Learning goals

  • Write factorial, Fibonacci, digit sum, reverse, power and GCD recursively
  • Explain why naive Fibonacci is exponential and how memoization fixes it
  • Use halving to turn O(n) power into O(log n)
  • Relate recursive binary search to the iterative one

Prerequisites

Recursion mechanicsInteger division and remainderArrays
5.02.1

Factorial

n! is n × (n - 1)! with 0! = 1. It has one recursive call per level, so n + 1 calls and a stack n + 1 frames deep. You saw its call stack in lesson 5.01.

Watch the range: 12! fits in an int, but 13! does not (6,227,020,800 exceeds 2,147,483,647). A long holds up to 20!. For larger values use BigInteger.

nn!Fits in
5120int
12479,001,600int
136,227,020,800long
202,432,902,008,176,640,000long
2151,090,942,171,709,440,000BigInteger only

Worst

O(n)

Space

O(n) stack

5.02.2

Fibonacci

fib(n) = fib(n - 1) + fib(n - 2) with fib(0) = 0 and fib(1) = 1. The recursion has two calls per level, so the tree branches. Run n = 6 in the first animation and look at the tree: fib(3) is computed three times and fib(2) five times.

The number of calls T(n) satisfies T(n) = T(n - 1) + T(n - 2) + 1, which grows like φⁿ with φ ≈ 1.618. The second animation adds a memo array. Each value is computed once, the tree collapses into a chain with cache hits (dashed green), and the cost drops to O(n).

nCalls with plain recursionCalls with memoization
1017719
2021,89139
302,692,53759
40331,160,28179

Fibonacci: naive recursion tree

Interactive · edit the input

Your input

Try

Recursion tree · 1 call

fib(5)…
Running now
Call

Call fib(5).

Step 1 / 60
Java
1static long fib(int n) {
2 if (n <= 1) return n; // base cases
3 return fib(n - 1) + fib(n - 2); // two recursive calls
4}

Live state

1 calls

n

5

depth

1

No condition evaluated on this step

Call stack (top first)

  1. fib(5)
Execution trace (dry run table)Show 60 steps
StepLineStateWhat happens
1L1n=5 depth=1Call fib(5).
2L2n=5Not a base case.
3L3n=5fib(5) = fib(4) + fib(3). Call fib(4) first.
4L1n=4 depth=2Call fib(4).
5L2n=4Not a base case.
6L3n=4fib(4) = fib(3) + fib(2). Call fib(3) first.
7L1n=3 depth=3Call fib(3).
8L2n=3Not a base case.
9L3n=3fib(3) = fib(2) + fib(1). Call fib(2) first.
10L1n=2 depth=4Call fib(2).
11L2n=2Not a base case.
12L3n=2fib(2) = fib(1) + fib(0). Call fib(1) first.
13L1n=1 depth=5Call fib(1).
14L2n=1Base case: fib(1) = 1.
15L2n=1 return=1return 1.
16L3n=2 left=1fib(1) = 1. Now call fib(0).
17L1n=0 depth=5Call fib(0).
18L2n=0Base case: fib(0) = 0.
19L2n=0 return=0return 0.
20L3n=2 left=1 right=0 return=1fib(2) = 1 + 0 = 1.
21L3n=3 left=1fib(2) = 1. Now call fib(1).
22L1n=1 depth=4Call fib(1).
23L2n=1Base case: fib(1) = 1.
24L2n=1 return=1return 1.
25L3n=3 left=1 right=1 return=2fib(3) = 1 + 1 = 2.
26L3n=4 left=2fib(3) = 2. Now call fib(2).
27L1n=2 depth=3Call fib(2).
28L2n=2Not a base case.
29L3n=2fib(2) = fib(1) + fib(0). Call fib(1) first.
30L1n=1 depth=4Call fib(1).
31L2n=1Base case: fib(1) = 1.
32L2n=1 return=1return 1.
33L3n=2 left=1fib(1) = 1. Now call fib(0).
34L1n=0 depth=4Call fib(0).
35L2n=0Base case: fib(0) = 0.
36L2n=0 return=0return 0.
37L3n=2 left=1 right=0 return=1fib(2) = 1 + 0 = 1.
38L3n=4 left=2 right=1 return=3fib(4) = 2 + 1 = 3.
39L3n=5 left=3fib(4) = 3. Now call fib(3).
40L1n=3 depth=2Call fib(3).
41L2n=3Not a base case.
42L3n=3fib(3) = fib(2) + fib(1). Call fib(2) first.
43L1n=2 depth=3Call fib(2).
44L2n=2Not a base case.
45L3n=2fib(2) = fib(1) + fib(0). Call fib(1) first.
46L1n=1 depth=4Call fib(1).
47L2n=1Base case: fib(1) = 1.
48L2n=1 return=1return 1.
49L3n=2 left=1fib(1) = 1. Now call fib(0).
50L1n=0 depth=4Call fib(0).
51L2n=0Base case: fib(0) = 0.
52L2n=0 return=0return 0.
53L3n=2 left=1 right=0 return=1fib(2) = 1 + 0 = 1.
54L3n=3 left=1fib(2) = 1. Now call fib(1).
55L1n=1 depth=3Call fib(1).
56L2n=1Base case: fib(1) = 1.
57L2n=1 return=1return 1.
58L3n=3 left=1 right=1 return=2fib(3) = 1 + 1 = 2.
59L3n=5 left=3 right=2 return=5fib(5) = 3 + 2 = 5.
60L3return=5fib(5) = 5 with 15 calls. Notice how fib(3) and its subtrees repeat. Memoization removes this waste.

Fibonacci with memoization

Interactive · edit the input

Your input

Try

Recursion tree · 1 call

fib(6)…
Running now
Call

Call fib(6).

Step 1 / 52
Java
1static long[] memo = new long[100];
2static long fib(int n) {
3 if (n <= 1) return n; // base cases
4 if (memo[n] != 0) return memo[n]; // already solved: reuse it
5 return memo[n] = fib(n - 1) + fib(n - 2); // solve once, remember
6}

Live state

1 calls

n

6

depth

1

No condition evaluated on this step

Call stack (top first)

  1. fib(6)
Execution trace (dry run table)Show 52 steps
StepLineStateWhat happens
1L2n=6 depth=1Call fib(6).
2L3n=6Not a base case.
3L4n=6 memo[n]=0memo[6] is empty, so compute it.
4L5n=6fib(6) = fib(5) + fib(4). Call fib(5) first.
5L2n=5 depth=2Call fib(5).
6L3n=5Not a base case.
7L4n=5 memo[n]=0memo[5] is empty, so compute it.
8L5n=5fib(5) = fib(4) + fib(3). Call fib(4) first.
9L2n=4 depth=3Call fib(4).
10L3n=4Not a base case.
11L4n=4 memo[n]=0memo[4] is empty, so compute it.
12L5n=4fib(4) = fib(3) + fib(2). Call fib(3) first.
13L2n=3 depth=4Call fib(3).
14L3n=3Not a base case.
15L4n=3 memo[n]=0memo[3] is empty, so compute it.
16L5n=3fib(3) = fib(2) + fib(1). Call fib(2) first.
17L2n=2 depth=5Call fib(2).
18L3n=2Not a base case.
19L4n=2 memo[n]=0memo[2] is empty, so compute it.
20L5n=2fib(2) = fib(1) + fib(0). Call fib(1) first.
21L2n=1 depth=6Call fib(1).
22L3n=1Base case: fib(1) = 1.
23L3n=1 return=1return 1.
24L5n=2 left=1fib(1) = 1. Now call fib(0).
25L2n=0 depth=6Call fib(0).
26L3n=0Base case: fib(0) = 0.
27L3n=0 return=0return 0.
28L5n=2 left=1 right=0 return=1fib(2) = 1 + 0 = 1. Store it in memo[2].
29L5n=3 left=1fib(2) = 1. Now call fib(1).
30L2n=1 depth=5Call fib(1).
31L3n=1Base case: fib(1) = 1.
32L3n=1 return=1return 1.
33L5n=3 left=1 right=1 return=2fib(3) = 1 + 1 = 2. Store it in memo[3].
34L5n=4 left=2fib(3) = 2. Now call fib(2).
35L2n=2 depth=4Call fib(2).
36L3n=2Not a base case.
37L4n=2 memo[n]=1memo[2] is already 1. No need to recurse again.
38L4n=2 return=1Cache hit: return 1 immediately. This whole subtree is skipped.
39L5n=4 left=2 right=1 return=3fib(4) = 2 + 1 = 3. Store it in memo[4].
40L5n=5 left=3fib(4) = 3. Now call fib(3).
41L2n=3 depth=3Call fib(3).
42L3n=3Not a base case.
43L4n=3 memo[n]=2memo[3] is already 2. No need to recurse again.
44L4n=3 return=2Cache hit: return 2 immediately. This whole subtree is skipped.
45L5n=5 left=3 right=2 return=5fib(5) = 3 + 2 = 5. Store it in memo[5].
46L5n=6 left=5fib(5) = 5. Now call fib(4).
47L2n=4 depth=2Call fib(4).
48L3n=4Not a base case.
49L4n=4 memo[n]=3memo[4] is already 3. No need to recurse again.
50L4n=4 return=3Cache hit: return 3 immediately. This whole subtree is skipped.
51L5n=6 left=5 right=3 return=8fib(6) = 5 + 3 = 8. Store it in memo[6].
52L5return=8fib(6) = 8 with 11 calls. Plain recursion would need 25 calls.
5.02.3

Sum of digits

To sum the digits of n, take the last digit n % 10 and add the digit sum of the rest, n / 10. The recursion stops at 0.

The number of calls equals the number of digits plus one, so the work is O(log₁₀ n). This pattern, peel off a piece and recurse on the remainder, is the same as walking a list.

Worst

O(log n)

Space

O(log n) stack

Sum of digits

Interactive · edit the input

Your input

Try

Recursion tree · 1 call

sumDigits(4821)…
Running now
Call

Call sumDigits(4821).

Step 1 / 20
Java
1static int sumDigits(int n) {
2 if (n == 0) return 0; // no digits left
3 return n % 10 + sumDigits(n / 10); // last digit + the rest
4}

Live state

1 calls

n

4821

No condition evaluated on this step

Call stack (top first)

  1. sumDigits(4821)
Execution trace (dry run table)Show 20 steps
StepLineStateWhat happens
1L1n=4821Call sumDigits(4821).
2L2n=4821Split 4821 into last digit 1 and the rest 482.
3L3n=4821 digit=1Call sumDigits(482) for the remaining digits.
4L1n=482Call sumDigits(482).
5L2n=482Split 482 into last digit 2 and the rest 48.
6L3n=482 digit=2Call sumDigits(48) for the remaining digits.
7L1n=48Call sumDigits(48).
8L2n=48Split 48 into last digit 8 and the rest 4.
9L3n=48 digit=8Call sumDigits(4) for the remaining digits.
10L1n=4Call sumDigits(4).
11L2n=4Split 4 into last digit 4 and the rest 0.
12L3n=4 digit=4Call sumDigits(0) for the remaining digits.
13L1n=0Call sumDigits(0).
14L2n=0Base case: no digits left.
15L2n=0 return=0return 0.
16L3n=4 return=44 + 0 = 4.
17L3n=48 return=128 + 4 = 12.
18L3n=482 return=142 + 12 = 14.
19L3n=4821 return=151 + 14 = 15.
20L3return=15The digits of 4821 sum to 15. Calls = number of digits + 1 = 5.
5.02.4

Reverse array

To reverse a[lo..hi], swap the two ends and reverse the inside a[lo + 1..hi - 1]. The base case is a range with zero or one element. The recursion depth is about n / 2.

An iterative two-pointer loop does the same with no stack. The recursive version is a good way to practise stating the invariant: positions outside lo..hi are already final.

Worst

O(n)

Space

O(n) stack

Reverse an array recursively

Interactive · edit the input

Your input

Try

int[] arr · length 7

1
2
3
4
5
6
7
0123456
Being comparedMovedIn final placeNot reached yet
Call

Call reverse(a, 0, 6).

Step 1 / 12
Java
1static void reverse(int[] a, int lo, int hi) {
2 if (lo >= hi) return; // zero or one element: nothing to do
3 swap(a, lo, hi); // fix the two outer elements
4 reverse(a, lo + 1, hi - 1); // reverse the inside
5}

Live state

1 calls0 swaps

lo

0

hi

6

No condition evaluated on this step

Call stack (top first)

  1. reverse(0, 6)
Execution trace (dry run table)Show 12 steps
StepLineStateWhat happens
1L1lo=0 hi=6Call reverse(a, 0, 6).
2L2lo=0 hi=6Two or more elements: swap the outer pair.
3L3lo=0 hi=6Swap a[0] and a[6]. Those two positions are now final.
4L1lo=1 hi=5Call reverse(a, 1, 5).
5L2lo=1 hi=5Two or more elements: swap the outer pair.
6L3lo=1 hi=5Swap a[1] and a[5]. Those two positions are now final.
7L1lo=2 hi=4Call reverse(a, 2, 4).
8L2lo=2 hi=4Two or more elements: swap the outer pair.
9L3lo=2 hi=4Swap a[2] and a[4]. Those two positions are now final.
10L1lo=3 hi=3Call reverse(a, 3, 3).
11L2lo=3 hi=3The pointers met or crossed. The middle needs no work.
12L4Reversed with 3 swaps and 4 calls. Recursion depth is about n / 2.
5.02.5

Power function

Computing x^n with n multiplications is O(n). Halving the exponent is much faster: x^n = (x^(n/2))² for even n and (x^(n/2))² · x for odd n. One recursive call with the smaller exponent is reused, not repeated.

The depth is ⌊log₂ n⌋ + 1. Try 2^16: only 5 calls and a handful of multiplications. Calling power(x, n / 2) twice instead of storing it in half would silently bring back O(n).

Worst

O(log n)

Space

O(log n) stack

Power function: halving the exponent

Interactive · edit the input

Your input

Try

Recursion tree · 1 call

power(2, 13)…
Running now
Call

Call power(2, 13).

Step 1 / 20
Java
1static long power(long x, int n) {
2 if (n == 0) return 1; // x^0 = 1
3 long half = power(x, n / 2); // solve a problem half the size
4 return (n % 2 == 0) ? half * half : half * half * x;
5}

Live state

1 calls0 multiplications

x

2

n

13

No condition evaluated on this step

Call stack (top first)

  1. power(2, 13)
Execution trace (dry run table)Show 20 steps
StepLineStateWhat happens
1L1x=2 n=13Call power(2, 13).
2L2n=13n = 13 is not 0. Halve it: 13 / 2 = 6.
3L3x=2 n=13Compute power(2, 6) once and reuse it.
4L1x=2 n=6Call power(2, 6).
5L2n=6n = 6 is not 0. Halve it: 6 / 2 = 3.
6L3x=2 n=6Compute power(2, 3) once and reuse it.
7L1x=2 n=3Call power(2, 3).
8L2n=3n = 3 is not 0. Halve it: 3 / 2 = 1.
9L3x=2 n=3Compute power(2, 1) once and reuse it.
10L1x=2 n=1Call power(2, 1).
11L2n=1n = 1 is not 0. Halve it: 1 / 2 = 0.
12L3x=2 n=1Compute power(2, 0) once and reuse it.
13L1x=2 n=0Call power(2, 0).
14L2n=0Base case: any number to the power 0 is 1.
15L2n=0 return=1return 1.
16L4n=1 half=1 return=2n is odd: half × half × x = 1 × 1 × 2 = 2.
17L4n=3 half=2 return=8n is odd: half × half × x = 2 × 2 × 2 = 8.
18L4n=6 half=8 return=64n is even: half × half = 8 × 8 = 64.
19L4n=13 half=64 return=8192n is odd: half × half × x = 64 × 64 × 2 = 8192.
20L4return=81922^13 = 8192 with 5 calls (about log₂ n) instead of 13 multiplications.
5.02.6

Greatest common divisor

Euclid's algorithm uses gcd(a, b) = gcd(b, a % b) and stops when b is 0. It works because any number that divides both a and b also divides the remainder a % b. Try 252 and 105: the pairs go (252, 105), (105, 42), (42, 21), (21, 0), so the answer is 21.

The numbers shrink at least by half every two steps, so it takes O(log min(a, b)) calls. The slowest inputs are consecutive Fibonacci numbers, such as the “Fibonacci pair” preset. The call is a tail call, so it is easy to write as a loop.

Worst

O(log min(a, b))

Space

O(log n) stack, O(1) as a loop

Greatest common divisor (Euclid)

Interactive · edit the input

Your input

Try

Recursion tree · 1 call

gcd(252, 105)…
Running now
Call

Call gcd(252, 105).

Step 1 / 16
Java
1static int gcd(int a, int b) {
2 if (b == 0) return a; // base case
3 return gcd(b, a % b); // same answer, smaller numbers
4}

Live state

1 calls

a

252

b

105

No condition evaluated on this step

Call stack (top first)

  1. gcd(252, 105)
Execution trace (dry run table)Show 16 steps
StepLineStateWhat happens
1L1a=252 b=105Call gcd(252, 105).
2L2a=252 b=105b is not 0. 252 % 105 = 42, and gcd(a, b) = gcd(b, a % b).
3L3a=252 b=105 a % b=42Call gcd(105, 42).
4L1a=105 b=42Call gcd(105, 42).
5L2a=105 b=42b is not 0. 105 % 42 = 21, and gcd(a, b) = gcd(b, a % b).
6L3a=105 b=42 a % b=21Call gcd(42, 21).
7L1a=42 b=21Call gcd(42, 21).
8L2a=42 b=21b is not 0. 42 % 21 = 0, and gcd(a, b) = gcd(b, a % b).
9L3a=42 b=21 a % b=0Call gcd(21, 0).
10L1a=21 b=0Call gcd(21, 0).
11L2a=21 b=0b is 0, so 21 divides everything left: the answer is 21.
12L2a=21 b=0 return=21return 21. Nothing more to do on the way back: this is a tail call.
13L3a=42 b=21 return=21return 21. Nothing more to do on the way back: this is a tail call.
14L3a=105 b=42 return=21return 21. Nothing more to do on the way back: this is a tail call.
15L3a=252 b=105 return=21return 21. Nothing more to do on the way back: this is a tail call.
16L3return=21gcd(252, 105) = 21 after 4 calls.

Practice ladder

Now try it yourself

Easy

  • Sum of an array recursively
  • Count occurrences of a value in an array recursively
  • Is a number a power of two (recursion by halving)?

Medium

  • Fibonacci with a memo array and compare call counts
  • Compute x^n modulo m with fast power
  • Find the maximum of an array recursively

Advanced

  • Fibonacci in O(log n) with matrix exponentiation
  • Extended Euclid: find x, y with ax + by = gcd(a, b)
  • Count the calls Euclid makes for consecutive Fibonacci inputs

Key takeaways

  • Naive Fibonacci recomputes the same subproblems and takes exponential time; memoization makes it linear.
  • Halving the problem (power, binary search, GCD) turns O(n) work into O(log n).
  • Call counts and stack depth are as important as the answer; always check overflow.