

[] [][] [][][]
public class Triangle
{
private int width;
public Triangle(int aWidth)
{
width = aWidth;
}
public int getArea()
{
. . .
}
}
public int getArea()
{
if (width == 1) {
return 1;
}
. . .
}
[] [][] [][][] [][][][]
Triangle smallerTriangle = new Triangle(width - 1); int smallerArea = smallerTriangle.getArea();
public int getArea()
{
if (width == 1) {
return 1;
}
Triangle smallerTriangle = new Triangle(width - 1);
int smallerArea = smallerTriangle.getArea();
return smallerArea + width;
}
A recursive
computation solves
a problem by using
the solution to the
same problem with
simpler inputs.
if (width <= 0) return 0;
1 + 2 + 3 + . . . + width
double area = 0; for (int i = 1; i <= width; i++) area = area + i;
1 + 2 + . . . + n = n × (n + 1)/2 => area = width * (width + 1) / 2
Area: 55 Expected: 55
[][][][]Of course, it would be simpler to compute the area simply as width * width. The results are identical because
[][][][]
[][][][]
[][][][]

public static boolean isLucky(int number)
{
int lastDigit = number % 10;
if (lastDigit == 8) { return true; }
else
{
return isLucky(number / 10); // Test the number without the last digit
}
}
public static int mystery(int n)
{
if (n <= 0) { return 0; }
else
{
int smaller = n - 1;
return mystery(smaller) + n * n;
}
}
What is mystery(4)?
mystery(4) calls mystery(3)
mystery(3) calls mystery(2)
mystery(2) calls mystery(1)
mystery(1) calls mystery(0)
mystery(0) returns 0.
mystery(1) returns 0 + 1 * 1 = 1
mystery(2) returns 1 + 2 * 2 = 5
mystery(3) returns 5 + 3 * 3 = 14
mystery(4) returns 14 + 4 * 4 = 30
To debug recursive methods with a debugger, you need to be particularly careful, and watch the call stack to understand which nested call you currently are in.

Thinking recursively is easy if you can recognize a subtask that is similar to the original task.
public class Sentence
{
private String text;
/**
Constructs a sentence.
@param aText a string containing all characters of the sentence
*/
public Sentence(String aText)
{
text = aText;
}
/**
Tests whether this sentence is a palindrome.
@return true if this sentence is a palindrome, false otherwise
*/
public boolean isPalindrome()
{
. . .
}
}
adam, I'm Ada, is a palindrome too!
public boolean isPalindrome()
{
int length = text.length();
// Separate case for shortest strings.
if (length <= 1) { return true; }
// Get first and last characters, converted to lowercase.
char first = Character.toLowerCase(text.charAt(0));
char last = Character.toLowerCase(text.charAt(length - 1));
if (Character.isLetter(first) && Character.isLetter(last))
{
// Both are letters.
if (first == last)
{
// Remove both first and last character.
Sentence shorter = new Sentence(text.substring(1, length - 1));
return shorter.isPalindrome();
}
else
{
return false;
}
}
else if (!Character.isLetter(last))
{
// Remove last character.
Sentence shorter = new Sentence(text.substring(0, length - 1));
return shorter.isPalindrome();
}
else
{
// Remove first character.
Sentence shorter = new Sentence(text.substring(1));
return shorter.isPalindrome();
}
}

/**
Tests whether a substring of the sentence is a palindrome.
@param start the index of the first character of the substring
@param end the index of the last character of the substring
@return true if the substring is a palindrome
*/
public boolean isPalindrome(int start, int end)
public boolean isPalindrome(int start, int end)
{
// Separate case for substrings of length 0 and 1.
if (start >= end) { return true; }
// Get first and last characters, converted to lowercase.
char first = Character.toLowerCase(text.charAt(start));
char last = Character.toLowerCase(text.charAt(end));
if (Character.isLetter(first) && Character.isLetter(last))
{
if (first == last)
{
// Test substring that doesn’t contain the matching letters.
return isPalindrome(start + 1, end - 1);
}
else
{
return false;
}
}
else if (!Character.isLetter(last))
{
// Test substring that doesn’t contain the last character.
return isPalindrome(start, end - 1);
}
else
{
// Test substring that doesn’t contain the first character.
return isPalindrome(start + 1, end);
}
}
public boolean isPalindrome()
{
return isPalindrome(0, text.length() - 1);
}
Enter n: 50
fib(1) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
. . .
fib(50) = 12586269025
Enter n: 6 Entering fib: n = 6 Entering fib: n = 5 Entering fib: n = 4 Entering fib: n = 3 Entering fib: n = 2 Exiting fib: n = 2 return value = 1 Entering fib: n = 1 Exiting fib: n = 1 return value = 1 Exiting fib: n = 3 return value = 2 Entering fib: n = 2 Exiting fib: n = 2 return value = 1 Exiting fib: n = 4 return value = 3 Entering fib: n = 3 Entering fib: n = 2 Exiting fib: n = 2 return value = 1 Entering fib: n = 1 Exiting fib: n = 1 return value = 1 Exiting fib: n = 3 return value = 2 Exiting fib: n = 5 return value = 5 Entering fib: n = 4 Entering fib: n = 3 Entering fib: n = 2 Exiting fib: n = 2 return value = 1 Entering fib: n = 1 Exiting fib: n = 1 return value = 1 Exiting fib: n = 3 return value = 2 Entering fib: n = 2 Exiting fib: n = 2 return value = 1 Exiting fib: n = 4 return value = 3 Exiting fib: n = 6 return value = 8 fib(6) = 8

Figure 2 Call Pattern of the Recursive fib Method
Enter n: 50
fib(1) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
. . .
fib(50) = 12586269025

public boolean isPalindrome()
{
int start = 0;
int end = text.length() - 1;
while (start < end)
{
char first = Character.toLowerCase(text.charAt(start));
char last = Character.toLowerCase(text.charAt(end);
if (Character.isLetter(first) && Character.isLetter(last))
{
// Both are letters.
if (first == last)
{
start++;
end--;
}
else
{
return false;
}
}
if (!Character.isLetter(last)) { end--; }
if (!Character.isLetter(first)) { start++; }
}
return true;
}

"eat" "eta" "aet" "ate" "tea" "tae"
eat eta aet ate tea tae
3 + 4 * 5 (3 + 4) * 5 1 - (2 - (3 - (4 - 5)))

Figure 3

public int getExpressionValue()
{
int value = getTermValue();
boolean done = false;
while (!done)
{
String next = tokenizer.peekToken();
if ("+".equals(next) || "-".equals(next))
{
tokenizer.nextToken(); // Discard "+" or "-"
int value2 = getTermValue();
if ("+".equals(next)) value = value + value2;
else value = value - value2;
}
else
{
done = true;
}
}
return value;
}
The getTermValue method calls getFactorValue in the same way, multiplying or dividing the factor values.
public int getFactorValue()
{
int value;
String next = tokenizer.peekToken();
if ("(".equals(next))
{
tokenizer.nextToken(); // Discard "("
value = getExpressionValue();
tokenizer.nextToken(); // Discard ")"
}
else
{
value = Integer.parseInt(tokenizer.nextToken());
}
return value;
}
Enter an expression: 3+4*5
3+4*5=23
Solve(partialSolution)
Examine(partialSolution).
If accepted
Add partialSolution to the list of solutions.
Else if continuing
For each p in extend(partialSolution)
Solve(p).
public class PartialSolution
{
private Queen[] queens;
public int examine() { . . . }
public PartialSolution[] extend() { . . . }
}
public int examine()
{
for (int i = 0; i < queens.length; i++)
{
for (int j = i + 1; j < queens.length; j++)
{
if (queens[i].attacks(queens[j])) { return ABANDON; }
}
}
if (queens.length == NQUEENS) { return ACCEPT; }
else { return CONTINUE; }
} public PartialSolution[] extend()
{
// Generate a new solution for each column
PartialSolution[] result = new PartialSolution[NQUEENS];
for (int i = 0; i < result.length; i++)
{
int size = queens.length;
// The new solution has one more row than this one
result[i] = new PartialSolution(size + 1);
// Copy this solution into the new one
for (int j = 0; j < size; j++)
{
result[i].queens[j] = queens[j];
}
// Append the new queen into the ith column
result[i].queens[size] = new Queen(size, i);
}
return result;
}
[a1, e2, h3, f4, c5, g6, b7, d8] [a1, f2, h3, c4, g5, d6, b7, e8] [a1, g2, d3, f4, h5, b6, e7, c8] . . . [f1, a2, e3, b4, h5, c6, g7, d8] . . . [h1, c2, a3, f4, b5, e6, g7, d8] [h1, d2, a3, c4, f5, b6, g7, e8](92 solutions)