Chapter 18

Recursion


Chapter Goals

Triangle Numbers

Outline of Triangle Class

public class Triangle
{
public Triangle(int aWidth)
{
width = aWidth;
}

public int getArea()
{
. . .
}

private int width;
}

Handling Triangle of Width 1

Handling the General Case

Completed getArea method

public int getArea()
{
if (width == 1) return 1;
Triangle smallerTriangle = new Triangle(width - 1);
int smallerArea = smallerTriangle.getArea();
return smallerArea + width;
}

Computing the area of a triangle with width 4

Recursion

Other Ways to Compute Triangle Numbers

File Triangle.java

TriangleTester.java


Output
   Enter width: 10
   Area = 55

Self Check

  1. Why is the statement if (width == 1) return 1; in the getArea method unnecessary?
  2. How would you modify the program to recursively compute the area of a square?

Answers

  1. Suppose we omit the statement. When computing the area of a triangle with width 1, we compute the area of the triangle with width 0 as 0, and then add 1, to arrive at the correct area.
  2. You would compute the smaller area recursively, then return
    smallerArea + width + width - 1.
    [][][][]
    [][][][]
    [][][][]
    [][][][]
    Of course, it would be simpler to compute

Permutations

Public Interface of PermutationGenerator

public class PermutationGenerator
{
public PermutationGenerator(String aWord) { . . . }
ArrayList<String> getPermutations() { . . . }
}

File PermutationGeneratorTester.java

Output

eat
eta
aet
ate
tea
tae

To Generate All Permutations

To Generate All Permutations

To Generate All Permutations

File PermutationGenerator.java

Self Check

  1. What are all permutations of the four-letter word beat?
  2. Our recursion for the permutation generator stops at the empty string. What simple modification would make the recursion stop at strings of length 0 or 1?

Answers

  1. They are b followed by the six permutations of eat, e followed by the six permutations of bat, a followed by the six permutations of bet, and t followed by the six permutations of bea.
  2. Simply change if (word.length() == 0) to if (word.length() <= 1), because a word with a single letter is also its sole permutation.

Tracing Through Recursive Methods


Thinking Recursively

Implement isPalindrome Method

public class Sentence
{
/**
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()
{
. . .
}

private String text;
}

Thinking Recursively: Step-by-Step

  1. Consider various ways to simplify inputs
    Here are several possibilities:

Thinking Recursively: Step-by-Step

  1. Combine solutions with simpler inputs into a solution of the original problem

Thinking Recursively: Step-by-Step

  1. Find solutions to the simplest inputs

Thinking Recursively: Step-by-Step

  1. Implement the solution by combining the simple cases and the reduction step

Recursive Helper Methods

Recursive Helper Methods

Recursive Helper Methods: isPalindrome

Self Check

  1. Do we have to give the same name to both isPalindrome methods?
  2. When does the recursive isPalindrome method stop calling itself?

Answers

  1. No–the first one could be given a different name such as substringIsPalindrome.
  2. When start >= end, that is, when the investigated string is either empty or has length 1.

Fibonacci Sequence

File FibTester.java

Output

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

The Efficiency of Recursion

File FibTrace.java

Output

Call Tree for Computing fib(6)


The Efficiency of Recursion

File FibLoop.java

Output

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

The Efficiency of Recursion

Iterative isPalindrome Method

Self Check

  1. You can compute the factorial function either with a loop, using the definition that n! = 1 × 2 × . . . × n, or recursively, using the definition that 0! = 1 and n! = (n - 1)! × n. Is the recursive approach inefficient in this case?
  2. Why isn't it easy to develop an iterative solution for the permutation generator?

Answers

  1. No, the recursive solution is about as efficient as the iterative approach. Both require n - 1 multiplications to compute n!.
  2. An iterative solution would have a loop whose body computes the next permutation from the previous ones. But there is no obvious mechanism for getting the next permutation. For example, if you already found permutations eat, eta, and aet, it is not clear how you use that information to get the next permutation. Actually, there is an ingenious mechanism for doing just that, but it is far from obvious–see Exercise P18.12.

The Limits of Computation


The Limits of Computation


Using Mutual Recursions

Syntax Diagram for Evaluating an Expression


Using Mutual Recursions

Syntax Tree for Two Expressions

Mutually Recursive Methods

The getExpressionValue Method

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 getFactorValue Method

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;
}

Using Mutual Recursions

To see the mutual recursion clearly, trace through the expression (3+4)*5:

File Evaluator.java

File ExpressionTokenizer.java

File EvaluatorTester.java


Output
   Enter an expression: 3+4*5
   3+4*5=23

Self Check

  1. What is the difference between a term and a factor? Why do we need both concepts?
  2. Why does the expression parser use mutual recursion?
  3. What happens if you try to parse the illegal expression 3+4*)5? Specifically, which method throws an exception?

Answers

  1. Factors are combined by multiplicative operators (* and /), terms are combined by additive operators (+, -). We need both so that multiplication can bind more strongly than addition.
  2. To handle parenthesized expressions, such as 2+3*(4+5). The subexpression 4+5 is handled by a recursive call to getExpressionValue.
  3. The Integer.parseInt call in getFactorValue throws an exception when it is given the string ")".