[] [][] [][][]
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)