6.3 Recursion: Base Cases, Tracing, Errors, and Iterative Equivalents

Key Takeaways

  • A correct recursive procedure has at least one base case that returns without recursing and a recursive call that moves the input toward a base case.
  • Each call gets its own stack frame with its own parameters and local variables; missing or unreachable base cases cause infinite recursion and a stack overflow.
  • To trace recursion, list the calls down to the base case, then substitute return values back up in reverse order.
  • Naive recursive Fibonacci makes an exponential number of calls because it recomputes the same subproblems; fib(4) alone makes 9 calls.
  • Every recursive algorithm has an iterative equivalent; simple linear recursions such as factorial become a loop with an accumulator.
Last updated: September 2026

What this competency asks

ETS asks you to understand simple recursive algorithms (for example, n factorial and the sum of the first n integers):

  1. Trace simple recursive algorithms.
  2. Provide missing steps in incomplete simple recursive algorithms.
  3. Identify parts of a recursive algorithm, such as the base (stopping) condition and the recursive call.
  4. Identify errors in simple recursive algorithms.
  5. Identify an iterative algorithm that is equivalent to a recursive algorithm.

The discussion questions also ask you to analyze the number of recursive calls.

Anatomy of a recursive procedure

A recursive procedure calls itself on a smaller version of the same problem.

int factorial ( int n )
    if ( n ≤ 1 )
        return 1                      // base case
    else
        return n * factorial ( n - 1 )  // recursive call on a smaller input
    end if
end factorial
PartRoleIn factorial
Base case (stopping condition)Answers the smallest input directly, without recursingn ≤ 1 returns 1
Recursive callSolves a smaller instancefactorial ( n - 1 )
Progress toward the base caseGuarantees the calls eventually stopn decreases by 1 each time
Combining stepBuilds the answer from the smaller resultn * …

Tracing recursion: down, then up

Each call waits for the call it makes. Write the calls going down, then fill in return values coming back up.

factorial ( 4 )

factorial(4) = 4 * factorial(3)
    factorial(3) = 3 * factorial(2)
        factorial(2) = 2 * factorial(1)
            factorial(1) = 1          ← base case
        factorial(2) = 2 * 1 = 2
    factorial(3) = 3 * 2 = 6
factorial(4) = 4 * 6 = 24

Four calls are active at the deepest point, one stack frame per call. Each frame holds its own copy of n. When a call returns, its frame is removed and the caller resumes where it left off.

Sum of the first n integers

int sumTo ( int n )
    if ( n == 0 )
        return 0
    else
        return n + sumTo ( n - 1 )
    end if
end sumTo

sumTo(4) = 4 + sumTo(3) = 4 + 3 + sumTo(2) = 4 + 3 + 2 + sumTo(1) = 4 + 3 + 2 + 1 + sumTo(0) = 4 + 3 + 2 + 1 + 0 = 10. That takes 5 calls, for n = 4, 3, 2, 1, and 0.

Supplying a missing step

To fill in a missing recursive step, write the relationship between the answer for n and the answer for a smaller input. For example, suppose a procedure should return the product of the integers from 3 through n (for n ≥ 3):

int prodFrom3 ( int n )
    if ( n == 3 )
        return 3
    else
        /* missing statement */
    end if
end prodFrom3

The product from 3 to n equals n times the product from 3 to n − 1, so the missing statement is return n * prodFrom3 ( n - 1 ). Check it with a small case: prodFrom3(5) = 5 × prodFrom3(4) = 5 × 4 × prodFrom3(3) = 5 × 4 × 3 = 60. Wrong choices typically recurse on the same n, which never ends, or jump too far, such as n - 3, which skips the base case.

Finding errors

ErrorExampleEffect
Missing base casereturn n + sumTo ( n - 1 ) with no ifInfinite recursion, then a stack overflow
Base case never reachedCalling factorial ( n + 1 ), or testing n == 0 when n can be negative or can skip 0Stack overflow
Wrong base valuereturn 0 as the base case of factorialEvery result becomes 0
Wrong combining stepreturn n * sumTo ( n - 1 ) in a sumComputes a product (and returns 0, from the base case)
No progressreturn f ( n )Calls itself with the same input forever

Counting calls: when recursion branches

int fib ( int n )
    if ( n ≤ 1 )
        return n
    else
        return fib ( n - 1 ) + fib ( n - 2 )
    end if
end fib

Each non-base call makes two calls, so the calls form a tree:

                 fib(4)
              /          \
         fib(3)          fib(2)
        /      \         /     \
    fib(2)   fib(1)  fib(1)  fib(0)
    /    \
fib(1)  fib(0)

Counting nodes gives 9 calls for fib(4). fib(2) is computed twice and fib(1) three times. The number of calls grows exponentially with n, roughly O(2ⁿ). Storing results that have already been computed (memoization, O(n) extra memory) or building up from fib(0) with a loop reduces the time to O(n).

Recursion and iteration

Any recursive algorithm can be rewritten iteratively. Simple linear recursions translate directly into a loop with an accumulator:

int factorialLoop ( int n )
    int result ← 1
    for ( int i ← 2; i ≤ n; i ← i + 1 )
        result ← result * i
    end for
    return result
end factorialLoop

To check that an iterative version is equivalent, compare them on the base case, for example n = 0 or 1 (both should return 1), and on a small value such as 4 (both should return 24).

RecursiveIterative
MemoryOne stack frame per active call: O(depth)Usually O(1) extra
Failure modeStack overflow when too deepInfinite loop
Natural fitTrees, divide-and-conquer, nested structuresSimple counting and accumulation

Some languages, such as Scheme, guarantee tail-call optimization. When the recursive call is the very last action, the language reuses the current frame, so deep tail recursion does not overflow the stack. Many popular languages, including Java and Python, do not perform this optimization.

A recursive algorithm you already know

Binary search (Section 6.1) is naturally recursive. Search the middle, then call the same procedure on the left or right half, with low > high as the base case for "not found." Because it makes only one recursive call per level, it makes about log₂ n calls, not an exponential number.

Test Your Knowledge

Consider this procedure, intended to return the sum 1 + 2 + … + n.

int total ( int n )
    return n + total ( n - 1 )
end total
What happens when total ( 5 ) is called?

A
B
C
D
Test Your Knowledge

What value is returned by mystery ( 4, 5 )?

int mystery ( int a, int b )
    if ( b == 0 )
        return 0
    else
        if ( b % 2 == 0 )
            return mystery ( a + a, b / 2 )
        else
            return a + mystery ( a, b - 1 )
        end if
    end if
end mystery

A
B
C
D
Test Your Knowledge

The procedure count ( n ) should return 1 + 2 + … + n for n ≥ 1. It begins if ( n == 1 ) return 1 else /* missing statement */ end if. Which statement correctly replaces /* missing statement */?

A
B
C
D
Test Your Knowledge

Using the recursive fib procedure that returns n when n ≤ 1 and otherwise returns fib(n − 1) + fib(n − 2), how many total calls to fib, including the first, are made when evaluating fib(4)?

A
B
C
D