Chapter 4: Recursion
Multiple Choice · What Is Recursion
Question 1: Termination
What guarantees that a recursive method terminates on every input?
B: The base case stops the recursion, and the recursive step must bring the input closer to it on every call (n − 1, n / 2 …). Neither the number of calls nor the return type decides termination, and a growing parameter never reaches the base case.
Short Answer · What Is Recursion
Question 2: Printing Before and After
What does f(3) print? Explain the order of the prints before and after the call.
void f(int n) { if (n == 0) return; System.out.print(n + " "); f(n - 1); System.out.print(n + " "); }
Show answer
Output: 3 2 1 1 2 3
- Line 3 runs before the call, printing 3, 2, 1 as the calls go down.
- f(0) is the base case and prints nothing.
- Line 5 runs after the call: as the frames are popped in reverse order, f(1), f(2), f(3) print 1, 2, 3.
Code Trace · The Call Stack
Question 3: Printing on the Way Back
What does printBinary(6) print, and what is the largest number of frames (including main) on the call stack?
main calls printBinary(6).
n = 6 ≠ 0 → line 3 calls printBinary(3); the print waits.
New frame with n = 3 → call printBinary(1).
n = 1 → 1 / 2 = 0: call printBinary(0).
Base case: return without printing. The stack is at its deepest: 5 frames.
printBinary(1) resumes where it left off: prints 1 % 2 = 1.
printBinary(3): prints 3 % 2 = 1.
printBinary(6): prints 6 % 2 = 0.
Output 110 (6 in binary); at most 5 frames. n halves with every call → O(log n).
void printBinary(int n) { if (n == 0) return; printBinary(n / 2); System.out.print(n % 2); } // main: printBinary(6);
Multiple Choice · The Call Stack
Question 4: An Unreachable Base Case
What do f(5) and f(6) produce?
int f(int n) { if (n == 1) return 1; return n * f(n - 2); }
B: f(5) = 5 · f(3) = 5 · 3 · f(1) = 15. f(6) goes 6, 4, 2, 0, −2 …; n never becomes 1, so the call stack fills up. With the base case n <= 1, f(6) would be 6 · 4 · 2 · 1 = 48.
Short Answer · The Call Stack
Question 5: Stack Depth
What does h(20) return? How many calls are made, and what is the largest number of frames (including main) on the call stack?
int h(int n) { if (n <= 1) return 0; return 1 + h(n / 2); }
Show answer
Calls: h(20) → h(10) → h(5) → h(2) → h(1). h(1) is the base case and returns 0.
On the way back each frame adds 1: h(2) = 1, h(5) = 2, h(10) = 3, h(20) = 4 (⌊log₂ 20⌋).
5 calls; at the deepest point 5 frames + main = 6. Since n halves with every call, time and memory are O(log n).
Short Answer · Linear Recursion
Question 6: Missing Line: The Largest Item
The method should return the largest of the first n items of the array. What goes in the blank? For a = {4, 9, 2, 7}, how many calls does largest(a, 4) make?
int largest(int[] a, int n) { if (n == 1) return a[0]; int m = largest(a, n - 1); return ______; }
Show answer
The blank: Math.max(m, a[n - 1])
m is the largest of the first n − 1 items, so all that is left is to compare it with the last item, a[n − 1].
Calls with n = 4, 3, 2, 1: 4 calls. On the way back: 4 → max(4, 9) = 9 → max(9, 2) = 9 → max(9, 7) = 9. T(n) = T(n − 1) + c → O(n).
Multiple Choice · Linear Recursion
Question 7: Fast Exponentiation
How many times in total is fastPower(2, 13) called (counting the first call)?
long fastPower(long x, int n) { if (n == 0) return 1; long y = fastPower(x, n / 2); if (n % 2 == 0) return y * y; return x * y * y; }
B: n takes the values 13, 6, 3, 1, 0 → 5 calls (⌊log₂ 13⌋ + 2). The result is 2¹³ = 8192. The slow power(2, 13) makes 14 calls.
Code Trace · Linear Recursion
Question 8: Tracing a Palindrome Check
What does the program print, and how many calls are made?
main calls isPalindrome("kiosk", 0, 4).
i = 0 < j = 4. s[0] = k, s[4] = k: equal → line 5 calls for the inner part.
s[1] = i, s[3] = s: different → base case, return false.
isPalindrome(0, 4) passes the false straight on (a tail call).
Output false; only 2 calls were made. The recursion stops at the first mismatch; in the worst case ⌊n/2⌋ + 1 calls, O(n).
boolean isPalindrome(String s, int i, int j) { if (i >= j) return true; if (s.charAt(i) != s.charAt(j)) return false; return isPalindrome(s, i + 1, j - 1); } // main: boolean p = isPalindrome("kiosk", 0, 4); System.out.println(p);
Multiple Choice · Binary Recursion
Question 9: Calls Made by fib(5)
For the recursive fib(n) (returns n if n <= 1, otherwise fib(n − 1) + fib(n − 2)), how many calls does fib(5) make in total?
C: The number of calls is C(n) = C(n − 1) + C(n − 2) + 1, with C(0) = C(1) = 1: C(2) = 3, C(3) = 5, C(4) = 9, C(5) = 15 (= 2 · F(6) − 1). 9 is the count for fib(4), and 25 for fib(6).
Short Answer · Binary Recursion
Question 10: Repeated Subproblems
In the call tree of fib(5), how many times are fib(3), fib(2), fib(1) and fib(0) called? What is the largest number of fib frames on the stack at once?
Show answer
| fib(5) | fib(4) | fib(3) | fib(2) | fib(1) | fib(0) | total |
|---|---|---|---|---|---|---|
| 1 | 1 | 2 | 3 | 5 | 3 | 15 |
fib(3) is computed twice and fib(2) three times: the same subproblem is solved again and again.
The deepest chain is fib(5) → fib(4) → fib(3) → fib(2) → fib(1): 5 frames. Exponential time, O(n) memory.
Storing the values in an array computes each fib(k) once → O(n).
Multiple Choice · Binary Recursion
Question 11: An Item That Isn't There
For a = {3, 8, 15, 21, 30, 42, 57}, how many calls does the recursive binarySearch(a, 20, 0, 6) make, and what does it return?
B: (0, 6) mid = 3, 20 < 21 → (0, 2) mid = 1, 20 > 8 → (2, 2) mid = 2, 20 > 15 → (3, 2) lo > hi: empty range, −1. The last call counts too: 4 calls. 3 is where 20 could be inserted, but the method returns −1.
Short Answer · Hanoi and Backtracking
Question 12: Hanoi Moves
How many lines does hanoi(4, 'A', 'B', 'C') print in total, and what is the 8th line? Justify your answer from the recursive structure.
void hanoi(int n, char from, char via, char to) { if (n == 1) { System.out.println("1: " + from + "→" + to); return; } hanoi(n - 1, from, to, via); System.out.println(n + ": " + from + "→" + to); hanoi(n - 1, via, from, to); }
Show answer
T(n) = 2T(n−1) + 1, T(1) = 1 → T(n) = 2ⁿ − 1. T(4) = 15 lines.
The call first moves the top 3 disks to B with hanoi(3, A, C, B): T(3) = 7 lines. Then the largest disk moves, so the 8th line is 4: A→C. The last 7 lines come from hanoi(3, B, A, C).
(Because n is even, the first move is 1: A→B.) Time O(2ⁿ); the call stack is at most n deep (+ main) → memory O(n).
Multiple Choice · Hanoi and Backtracking
Question 13: Hanoi: Line 5
What is line 5 of the output of hanoi(3, 'A', 'B', 'C')? (If n == 1 it prints "1: from→to"; otherwise hanoi(n − 1, from, to, via), "n: from→to", hanoi(n − 1, via, from, to).)
C: The first 3 lines come from hanoi(2, A, C, B) (1: A→C, 2: A→B, 1: C→B); line 4 is the largest disk: 3: A→C. The remaining lines come from hanoi(2, B, A, C), and the first of them is hanoi(1, B, C, A): 1: B→A.
Short Answer · Hanoi and Backtracking
Question 14: Order of the Branches
When generating subsets, each item has two choices: take it (a[i] is added to the subset) or leave it (a[i] is not added). The slides' subsets tries “take” first; the sub method below tries “leave” first. What does sub(new int[]{1, 2, 3}, 0, new ArrayList<>()) print? How does changing the order change the output?
void sub(int[] a, int i, List<Integer> s) { if (i == a.length) { System.out.print(s + " "); return; } sub(a, i + 1, s); // 1) leave: without a[i] s.add(a[i]); sub(a, i + 1, s); // 2) take: with a[i] s.remove(s.size() - 1); }
Show answer
Output: [] [3] [2] [2, 3] [1] [1, 3] [1, 2] [1, 2, 3]
At every node the “leave” branch comes first. The slides' subsets takes the “take” branch first and prints [1, 2, 3] [1, 2] [1, 3] [1] [2, 3] [2] [3] [], so the output is exactly reversed. The subsets themselves are the same: still 8 subsets and 15 calls.
The undo (s.remove) comes after the second call, so s is back to what it was when the method started.
Multiple Choice · Hanoi and Backtracking
Question 15: Forgetting to Undo
If the line s.remove(s.size() - 1); is deleted from the subsets method, what is the output for {1, 2}?
void subsets(int[] a, int i, List<Integer> s) { if (i == a.length) { System.out.print(s + " "); return; } s.add(a[i]); subsets(a, i + 1, s); // the s.remove(...) line was deleted subsets(a, i + 1, s); }
B: No choice is ever undone. The left branch prints [1, 2], and the right branch sees the same list: [1, 2]. When the root moves to its right branch, s is still [1, 2]; 2 is added again, and [1, 2, 2] is printed twice. The correct output would be A.
Short Answer · Hanoi and Backtracking
Question 16: Counting Permutations
At each level, the perm method below picks one of the letters not yet used, from left to right. For perm("ABCD".toCharArray(), new boolean[4], new StringBuilder()), how many permutations are printed, how many calls are made, and what are the first three outputs?
void perm(char[] c, boolean[] used, StringBuilder s) { if (s.length() == c.length) { // base System.out.print(s + " "); return; } for (int k = 0; k < c.length; k++) { if (used[k]) continue; // used used[k] = true; s.append(c[k]); // choose perm(c, used, s); used[k] = false; // undo s.deleteCharAt(s.length() - 1); } }
Show answer
4! = 24 permutations.
Levels of the call tree: 1 (empty) + 4 + 4 · 3 + 4 · 3 · 2 + 4 · 3 · 2 · 1 = 1 + 4 + 12 + 24 + 24 = 65 calls.
First outputs: ABCD ABDC ACBD. Stack depth n + 1 = 5 frames; time O(n · n!).
Multiple Choice · Recurrence Relations
Question 17: Recognizing the Recurrence
Which is the recurrence for this method, and its solution?
void print(int n) { if (n == 0) return; print(n - 1); print(n - 1); System.out.print(n); }
B: Each call makes two calls of size n − 1; level k has 2ᵏ calls, 2ⁿ⁺¹ − 1 calls in total. print(3) makes 15 calls and prints 1121123.
Short Answer · Recurrence Relations
Question 18: Solving with a Call Tree
What does count(8) return, and how many calls does it make in total? What is the largest number of count frames on the stack at once? Draw the call tree level by level.
int count(int n) { if (n <= 1) return 1; return count(n / 2) + count(n / 2); }
Show answer
| Level | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| n | 8 | 4 | 2 | 1 |
| calls | 1 | 2 | 4 | 8 |
In total 1 + 2 + 4 + 8 = 15 calls (2n − 1). Each of the 8 leaves returns 1: count(8) = 8.
The deepest chain is 8 → 4 → 2 → 1: 4 frames (log₂ n + 1). T(n) = 2T(n/2) + c → O(n) time, O(log n) memory.
Multiple Choice · Tail Recursion
Question 19: Which One Is Tail Recursive?
Which of these recursive steps is tail recursive?
C: The call is the method's last action; the addition happens before the call, and the returned value is passed on unchanged. In A and B a multiplication or addition happens after the call returns, and in D a print does.
Short Answer · Tail Recursion
Question 20: Converting to a Loop
Through which chain of calls does sumTail(4, 0) return, and what value? Write the loop equivalent of the method. Which one uses less memory in Java?
int sumTail(int n, int acc) { if (n == 0) return acc; return sumTail(n - 1, acc + n); }
Show answer
(4, 0) → (3, 4) → (2, 7) → (1, 9) → (0, 10): the base case returns acc = 10, which is passed back unchanged; 5 calls.
int sumLoop(int n) { int acc = 0; while (n != 0) { acc = acc + n; n = n - 1; } return acc; }
Java does not optimize tail calls: the recursive version uses O(n) stack, the loop O(1).
Multiple Choice · Recursion on Linked Lists
Question 21: Recursion over a List
If the list is 5 → 8 → 2 → 3 → null, what does f(head) print?
void f(Node node) { if (node == null) return; if (node.data % 2 == 0) System.out.print(node.data + " "); f(node.next); if (node.data % 2 == 1) System.out.print(node.data + " "); }
A: Even values are printed before the call, in list order: 8 2. Odd values are printed after the call, that is, from last to first as the stack unwinds: 3 5.
Open-Ended Question · Recursion on Linked Lists
Question 22: Summing a Long List
A sensor appends its readings to a singly linked list that can hold a million nodes. To total the readings, should you use a recursive method of the form return node.data + sum(node.next); or a loop? Justify your answer.
Sample answer
The recursive version is short and close to the definition, but it pushes one frame per node: a depth of n = 10⁶. The default Java stack cannot hold that many frames → StackOverflowError.
Even written tail recursively (with an accumulator), the problem remains, because Java does not optimize tail calls.
A loop (for (Node node = head; node != null; node = node.next) total += node.data;) runs in O(n) time and O(1) memory. Since the total may overflow, use a long.