Chapter 8 Recursion
output statements
- Recursive methods can be particularly challenging to debug. -Adding output statements can be helpful. -Furthermore, an additional trick is to indent the print statements to show the current depth of recursion. -System.out.print(indentAmt + ...);. Indent is typically some number of spaces. main() sets indent to three spaces. Each recursive call adds three more spaces. -Some programmers like to leave the output statements in the code, commenting them out with "//" when not in use. The statements actually serve as a form of comment as well.
Recursively searching a sorted list:
-Search is commonly performed to quickly find an item in a sorted list stored in an array or ArrayList. -findMatch() restricts its search to elements within the range lowVal to highVal. -main() initially passes a range of the entire list: 0 to (list size - 1). -findMatch() compares to the middle element, returning that element's position if matching. If not matching, findMatch() checks if the window's size is just one element, returning -1 in that case to indicate the item was not found. -If neither of those two base cases are satisfied, then findMatch() recursively searches either the lower or upper half of the range as appropriate.
recursive method
-a method that calls itself -a natural match for the recursive binary search algorithm. -has an if-else statement
1) Recursive GCD example. How many calls are made to gcdCalculator() method for input values 12 and 8? 1 2 3 2) What is the base case for the GCD algorithm? When both inputs to the method are equal. When both inputs are greater than 1. When inNum1 > inNum2.
1) 3 First call compares 12 and 8, second call compares 4 and 8, third call compares 4 and 4. Because the numbers are equal the base case is detected, and 4 is returned. 2) When both inputs to the method are equal When both inputs are equal, the greatest common divisor is found.
1) If a sorted list has elements 0 to 50 and the item being searched for is at element 6, how many times will findMatch() be called? 2) If an alphabetically ascending list has elements 0 to 50, and the item at element 0 is "Bananas", how many calls to findMatch() will be made during the failed search for "Apples"?
1) 3 First call: findMatch(0, 50), which checks element (0 + 50) / 2 = 25. Second call: findMatch(0, 25), which checks element 12 (0 + 25) / 2 = 12 (the fraction is truncated). Third call: findMatch(0, 12), which checks element 6 and finds the element. 2) 7 First call: findMatch(0, 50). Subsequent calls will pass these (lowVal, highVal) value pairs: (0, 25), (0, 12), (0, 6), (0, 3), (0, 1), (0, 0). At (0, 1) the midVal is 0 because Apples is less than the item at element 0 (Bananas). The next call is findMatch(0, 0). Because the rangeSize is now 1, the second base case is reached and the method returns -1.
Before writing a recursive method, a programmer should determine:
1. Does the problem naturally have a recursive solution? 2. Is a recursive solution better than a non-recursive solution?
Creating a recursive method can be accomplished in two steps:
1. Write the base case -- Every recursive method must have a case that returns a value without performing a recursive call. That case is called the base case. A programmer may write that part of the method first, and then test. There may be multiple base cases. 2. Write the recursive case -- The programmer then adds the recursive case to the method.
Binary Search Example: A friend thinks of a number from 0 to 100 and you try to guess the number, with the friend telling you to guess higher or lower until you guess correctly
A recursive function is a natural match for the recursive binary search algorithm. A function GuessNumber(lowVal, highVal) has parameters that indicate the low and high sides of the guessing range. 1. Using a binary search algorithm, you begin at the midpoint of the lower range. (highVal + lowVal) / 2 = (100 + 0) / 2, or 50. 2. The number is lower. The algorithm divides the range in half, then chooses the midpoint of that range. 3. After each guess, the binary search algorithm is applied, halving the range and guessing the midpoint or the corresponding range. 4. A recursive function is a natural match for the recursive binary search algorithm. A function GuessNumber(lowVal, highVal) has parameters that indicate the low and high sides of the guessing range.
Greatest common divisor (GCD)
Recursion can solve the greatest common divisor problem. The greatest common divisor (GCD) is the largest number that divides evenly into two numbers, e.g. GCD(12, 8) = 4. One GCD algorithm (described by Euclid around 300 BC) subtracts the smaller number from the larger number until both numbers are equal. Ex: -GCD(12, 8): Subtract 8 from 12, yielding 4. -GCD(4, 8): Subtract 4 from 8, yielding 4. -GCD(4, 4): Numbers are equal, return 4
stack frame
Recursion enables an elegant solution to some problems. But, for large problems, deep recursion can cause memory problems. Part of a program's memory is reserved to support function calls. Each method call places a new stack frame on the stack, for local parameters, local variables, and more method items. Upon return, the frame is deleted.
Recursive exploration of all possibilities
Recursion is a powerful technique for exploring all possibilities, such as all possible reorderings of a word's letters, all possible subsets of items, all possible paths between cities, etc.
Fibonacci sequence
Recursive methods can solve certain math problems, such as computing the Fibonacci sequence. The Fibonacci sequence is 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, etc.; starting with 0, 1, the pattern is to compute the next number by adding the previous two numbers.
recursive algorithm
an algorithm that breaks the problem into smaller subproblems and applies the same algorithm to solve the smaller subproblems.
Binary Search Algorithm
begins at the midpoint of the range and halves the range after each guess. For example: Is it 50 (the middle of 0-100)? Lower Is it 25 (the middle of 0-50)? Higher Is it 37 (the middle of 25-50)? Lower Is it 31 (the middle of 25-37). After each guess, the binary search algorithm is applied again, but on a smaller range, i.e., the algorithm is recursive.
1) A memory's stack region can store at most one stack frame. True False 2)The size of the stack is unlimited. True False 3) A stack overflow occurs when the stack frame for a method call extends past the end of the stack's memory. True False 4) The following recursive method will result in a stack overflow. int recAdder(int inValue) { return recAdder(inValue + 1); } True False
1) False Each method call gets its own frame. Each frame has local method items like parameters, local variables, and the return value. . 2) False Memory is limited, so clearly stack size must be limited. Deep recursion could overflow the stack. 3) True Such stack overflow usually causes the program to crash. 4) True The method continues to make recursive calls. If the argument to the first call is 5, the recursive call passes 6, then 7, then 8, etc. Eventually, stack overflow occurs.
Recursive methods can be accomplished in one step, namely repeated calls to itself. True False 2) A recursive method with parameter N counts up from any negative number to 0. An appropriate base case would be N == 0. True False 3) A recursive method can have two base cases, such as N == 0 returning 0, and N == 1 returning 1. True False
1) False Recursive methods are accomplished in two steps. (1) A base case returns a value without performing a recursive call. (2) The recursive case calls itself. 2) True The method stops making recursive calls when reaching 0. Sample output would be -2, -1, 0. 3) True A recursive method may have multiple base cases.
Which are recursive definitions/algorithms? 1) Helping N people:If N is 1, help that person.Else, help the first N/2 people, then help the second N/2 people. True False 2) Driving to the store:Go 1 mile.Turn left on Main Street.Go 1/2 mile. True False 3) Sorting envelopes by zipcode:If N is 1, done. Else, find the middle zipcode. Put all zipcodes less than the middle zipcode on the left, all greater ones on the right. Then sort the left, then sort the right. True False
1) True Base case is to help 1 person. Recursive applications are to help the first N/2, then the second N/2. 2) False There is no recursive call to "driving to the store". 3) True The base case is that there is only 1 envelope (which itself is "sorted"). The recursive calls are to sort the lower numbers, then sort the higher numbers. This recursive process actually works well for sorting envelopes, or a stack of papers with people's names, for example.
A list has 5 elements numbered 0 to 4, with these letter values: 0: A, 1: B, 2: D, 3: E, 4: F. 1) To search for item C, the first call is findMatch(0, 4). What is the second call to findMatch()? findMatch(0, 0) findMatch(0, 2) findMatch(3, 4) 2) In searching for item C, findMatch(0, 2) is called. What happens next? Base case 1: item found at midVal. Base case 2: rangeSize == 1, so no match. Recursive call: findMatch(2, 2)
1) findMatch(0, 2) findMatch(0, 4) checks list[2]. C < D so the search proceeds left calling the method to explore the lower half or findMatch(0, 2). 2) Recursive call: findMatch(2, 2) findMatch(0, 2) checks list[1]. C > B so the search proceeds to the right, so findMatch(1 + 1, 2) or findMatch(2, 2). That call will return -1 (no match).
1)What is the output of scrambleLetters("xy", "")? Determine your answer by manually tracing the code, not by running the program. yx xy xx yy xy yx xy yx
1) xy yx The first call selects x, then makes a recursive call with y remaining, yielding xy. That first call then selects y, then makes a recursive call with x remaining, yielding yx.
Typically, programmers will use two methods for recursion:
1. An "outer" method is intended to be called from other parts of the program, like the method int calcFactorial(int inVal). 2. An "inner" method is intended only to be called from that outer method, for example a method int calcFactorialHelper(int inVal). -The outer method may check for a valid input value, e.g., ensuring inVal is not negative, and then calling the inner method. -Commonly, the inner method has parameters that are mainly of use as part of the recursion, and need not be part of the outer method, thus keeping the outer method more intuitive.
Common Error:
A common error is to not cover all possible base cases in a recursive method. Another common error is to write a recursive method that doesn't always reach a base case. Both errors may lead to infinite recursion, causing the program to fail.
base case
At some point, a recursive algorithm must describe how to actually do something -if branch ends the recursion
N factorial (N!) is commonly implemented as a recursive method due to being easier to understand and executing faster than a loop implementation. True False
False While N! is widely used to introduce recursion, a loop implementation is likely more intuitive and faster too. N! is just a simple example for recursion; other algorithms are a better match for a recursive solution.
A recursive method example. public class CountDownTimer { public static void countDown(int countInt) { if (countInt <= 0) { System.out.println("GO!"); } else { System.out.println(countInt); countDown(countInt-1); } } public static void main (String[] args) { countDown(2); } }
Output: 2 1 Go! ** 1. The first call to countDown( ) method comes from main. Each call to countDown( ) effectively creates a new "copy" of the executing method, as shown on the right. 2. Then, the countDown( ) function calls itself. countDown(1) similarly creates a new "copy" of the executing method. 3. countDown( ) method calls itself once more. 4. That last instance does not call countDown( ) again, but instead returns. As each instance returns, that copy is deleted.
Recursive search method guessNumber(lowVal, highVal, scnr)
has parameters that indicate the low and high sides of the guessing range and a Scanner object for getting user input. The method guesses at the midpoint of the range. If the user says lower, the method calls guessNumber(lowVal, midVal, scnr). If the user says higher, the method calls guessNumber(midVal + 1, highVal, scnr)
an algorithm
is a sequence of steps for solving a problem.
stack overflow
meaning a stack frame extends beyond the memory region allocated for stack, Stack overflow usually causes the program to crash and report an error like: stack overflow error or stack overflow exception. Deep recursion may cause stack overflow, causing a program to crash.