Visualize Recursion
Sometimes you would like to see what recursion is doing.
This document shows you how to use polymorphism to add “print” statements without changing your recursion code!
This way, you don’t complicate your code with a lot of
System.out.println
stuff.
As a bonus, most of the code is reusable.
Example Problem
Let’s do the fibonacci problem on codingbat.com.
fibonacci(int n)
- return the n-th Fibonacci number.fibonacci(0) = 0, fibonacci(1) = 1, and fibonacci(n) = fibonacci(n-1) + fibonacci(n-2)
This is a hard problem for recursion because the complexty increases geometrically.
Think in Code
Before writing the code, write comments for what you want to do.
public int fibonacci(int n) {
// 1. base case: check stopping criteria (success or failure)
// Two base cases: n <= 0 and n == 1.
// You should check n <= 0 and NOT n == 0 to guarantee
// recursion always stops.
// 2. recursive case. Apply the Fibonacci formula.
// 3. guaranteed to stop?
}
Add the code:
public int fibonacci(int n) {
// 1. base case: check stopping criteria (success or failure)
// Two base cases: n <= 0 and n == 1.
if (n <= 0) return 0;
// Another base case:
if (n == 1) return 1;
// 2. recursive case
return fibonacci(n-1) + fibonacci(n-2);
// 3. guaranteed to stop?
// Yes because each time we call fibonacci(n) the value of n
// is decreasing, so it will eventually become <= 0, the base case.
}
The code works, but we can’t see what’s happening. Let’s add print statements – without changing the code.
1. Copy the code into a class named Recursion
public class Recursion {
// comments omitted to save space
public int fibonacci(int n) {
if (n <= 0) return 0;
if (n == 1) return 1;
return fibonacci(n-1) + fibonacci(n-2);
}
}
When fibonacci
calls itself, what it is really calling is:
return this.fibonacci(n-1) + this.fibonacci(n-2);
You don’t write this.
because its automatic in Java.
Here’s the trick: write a subclass that overrides fibonacci
and
adds print statements. Then the invisible this
will be the subclass.
3. Subclass Overrides Fibonacci
public class RecursionWithPrint extends Recursion {
@Override
public int fibonacci(int n) {
System.out.printf("call fibonacci(%d)\n", n);
int result = super.fibonacci(n);
System.out.printf("return %d\n", result);
return result;
}
public static void main(String[] args) {
RecursionWithPrint rwp = new RecursionWithPrint();
// Compute a fibonacci number
int n = 4;
System.out.printf("fibonacci(%d)\n", n);
System.out.println( rwp.fibonacci(n) );
}
}
The output is:
fibonacci(4)
call fibonacci(4)
call fibonacci(3)
call fibonacci(2)
call fibonacci(1)
return 1
call fibonacci(0)
return 0
return 1
call fibonacci(1)
return 1
return 2
call fibonacci(2)
call fibonacci(1)
return 1
call fibonacci(0)
return 0
return 1
return 3
3
Output would be easier to understand if recursive calls where indented.
Let’s add indentation and also make the code reusable, so we can apply it to other recursion problems:
public class RecursionWithPrint extends Recursion {
// chars to print for indentation.
static final String INDENT = " "; // 4 spaces
// recursion level
private int level = 0;
public void enter(String format, Object ... args) {
for(int k=0; k<level; k++) System.out.print(INDENT); // indent the message
level++;
System.out.printf(format, args);
System.out.println();
}
public void leave(Object value) {
level--;
for(int k=0; k<level; k++) System.out.print(INDENT); // indent the message
System.out.println("return "+value);
}
@Override
public int fibonacci(int n) {
enter("call fibonacci(%d)", n);
int result = super.fibonacci(n);
leave(result);
return result;
}
public static void main(String[] args) {
RecursionWithPrint rwp = new RecursionWithPrint();
int n = 4;
System.out.printf("fibonacci(%d)\n", n);
System.out.println( rwp.fibonacci(n) );
}
}
Output:
Compute fibonacci(4)
call fibonacci(4)
call fibonacci(3)
call fibonacci(2)
call fibonacci(1)
return 1
call fibonacci(0)
return 0
return 1
call fibonacci(1)
return 1
return 2
call fibonacci(2)
call fibonacci(1)
return 1
call fibonacci(0)
return 0
return 1
return 3
3