Recursion
A Method That Calls Itself
Master the art of recursive thinking: learn how methods call themselves to solve complex problems by breaking them into smaller, identical subproblems.
What Is Recursion?
Recursion is a programming technique where a method calls itself to solve a problem. Instead of using loops, a recursive method breaks a problem into smaller instances of the same problem until it reaches a case simple enough to solve directly.
The Two Essential Parts
The Call Stack in Action
Tracing an array sum
This separate worked question comes from the related lesson.
A student writes the following recursive method intended to compute the sum of an int array: public static int sumArray(int[] arr, int index) { if (index == arr.length) return 0; return arr[index] + sumArray(arr, index + 1); } (a) Trace the execution of sumArray(new int[]{5, 3, 8}, 0), showing every recursive call and its return value. (b) The student's friend claims the method would work just as well if the base case were changed to if (index == arr.length - 1) return arr[index];. Is the friend correct? Explain whether the modified version produces the same results for all valid inputs, including edge cases. (c) Rewrite the method so that it processes the array from the last element toward the first (i.e., the initial call is sumArray(arr, arr.length - 1) and the base case occurs at index 0). (d) Explain the time and space complexity of this recursive approach and compare it to an iterative for-loop summing the same array.
Show answer and explanation
(a) Trace: sumArray({5,3,8}, 0): index 0 ≠ 3 → returns 5 + sumArray({5,3,8}, 1) sumArray({5,3,8}, 1): index 1 ≠ 3 → returns 3 + sumArray({5,3,8}, 2) sumArray({5,3,8}, 2): index 2 ≠ 3 → returns 8 + sumArray({5,3,8}, 3) sumArray({5,3,8}, 3): index 3 == 3 → returns 0 (base case) Unwinding: 8+0=8, 3+8=11, 5+11=16. Final result: 16. (b) The friend is partially correct. For any non-empty array, the modified base case returns arr[arr.length-1] directly instead of recursing one more level and adding arr[arr.length-1]+0. The arithmetic result is identical. However, for an empty array (length 0) called with index 0, the condition 0 == -1 is false. The next expression first accesses arr[0], throwing an ArrayIndexOutOfBoundsException before recursion, while the original correctly returns 0. So the friend's version is not equivalent for all valid inputs. (c) Reverse implementation: public static int sumArray(int[] arr, int index) { if (index < 0) return 0; if (index == 0) return arr[0]; return arr[index] + sumArray(arr, index - 1); } For a nonnull array, use the initial call sumArray(arr, arr.length - 1). An empty array starts at index -1 and returns 0. For a nonempty array, the base case at index 0 returns arr[0]; each recursive call decrements the index. (d) Complexity: Both the recursive and iterative versions run in O(n) time because each element is visited exactly once. However, the recursive version requires O(n) space on the call stack (one frame per element), while a for-loop uses only O(1) extra space for the accumulator variable. For very large arrays, the iterative version is preferable to avoid stack overflow.
![For sumArray([5,3,8],0), calls reach index 3 and return 0, then unwind through 8, 11 and 16.](https://ifrednwdtrrkakmaumxy.supabase.co/storage/v1/object/public/ContentGraphics/ap-stem/webp/sha256/b0/b0a30320ed8759b2a145838a0211513bad13120234019d89a5a4561d355f083c.webp)
The Recursive Pattern
Classic Example: Factorial
The factorial of \( n \) is defined as \( n! = n \times (n-1) \times \cdots \times 1 \), with the base case \( 0! = 1 \). This maps perfectly to recursion:
Recursion vs. Iteration
| Recursion | Iteration | |
|---|---|---|
| Mechanism | Method calls itself | Loops (for, while) |
| Termination | Base case | Loop condition |
| Memory | Uses call stack | Uses loop variables |
| Readability | Elegant for trees, math | Simpler for linear tasks |
| Risk | StackOverflowError | Infinite loop |
How to Write a Recursive Method
Example 1: Sum of Digits
Write a method that returns the sum of the digits of a non-negative integer. For example, sumDigits(1234) should return \( 1 + 2 + 3 + 4 = 10 \).
Example 2: Computing \( b^n \)
Compute \( b^n \) recursively using the fact that \( b^n = b \times b^{n-1} \) with base case \( b^0 = 1 \).
Example 3: Fibonacci Sequence
The Fibonacci sequence is defined as \( F(0) = 0 \), \( F(1) = 1 \), and \( F(n) = F(n-1) + F(n-2) \). Note: this has two recursive calls and two base cases.
Example 4: String Reversal
Recursion works on Strings too. Reverse a String by peeling off the first character and appending it at the end of the reversed remainder.
Example 5: Recursive Binary Search
Common Mistakes
Recursion Summary
| Concept | Key Idea | Example |
|---|---|---|
| Base Case | Simplest input, returns directly | n == 0 → return 1 |
| Recursive Case | Calls itself with smaller input | n * factorial(n-1) |
| Call Stack | Each call waits for the next | LIFO order |
| Termination | Must reach base case | Else StackOverflow |
| Best Use | Naturally recursive structures | Trees, divide-and-conquer |
Question 1 of 5
What is the output of the following code?
Question 2 of 5
Which of the following will cause a StackOverflowError?
Question 3 of 5
What does the following method return when called as foo("hello")?
Question 4 of 5
How many times is fib() called in total (including the original call) when computing fib(5)?
Question 5 of 5
What is the output of the following recursive method call mystery(4)?