java programming: from the ground up chapter 8 recursion
TRANSCRIPT
Java Programming:From the Ground Up
Chapter 8Recursion
A Simple Recursive Method
1. public static void forgetMeNot()2. { 3. System.out.println(“I Miss You”); 4. forgetMeNot(); // a recursive call5. } • The method prints “I Miss You” and then calls itself infinitely:
I Miss YouI Miss YouI Miss You…( forever!)
A Simple Recursive Method
Problem Statement
Write a recursive method that prints “I Miss You” n times and signs off “Love, Me”.
A Simple Recursive Method
1. public class GreetingCard2. {3. public static void forgetMeNot(int n) // n is the number of times the message is printed.1. { 2. if (n != 0) 3. { 4. System.out.println(“I Miss You”); 5. forgetMeNot(n – 1 ); // the recursive call6. }7. }8. public static void main(String[] args)9. {10. forgetMeNot(3); // invokes method for the first time11. System.out.println();12. System.out.println(“Love, Me”);13. }14. }
A Simple Recursive Method
Output
I Miss YouI Miss YouI Miss YouLove, Me
The recursive method forgetMeNot(...) is called by main(...) and passed the argument 3.
Tracing a Recursive Method
• Main method begins with the call forgetMeNot(3): prints “I Miss you” • Calls forgetMeNot(2): prints “I Miss You” • Calls forgetMeNot(1): prints “I Miss You” • Calls forgetMeNot(0): does nothing because n!=0 evaluates to false • forgetMeNot(0) returns and the previous call • forgetMeNot(1), picks up where it left off • forgetMeNot(1) has nothing more to do so it exits• Program control returns to the suspended forgetMeNot(2) which exits• Control returns to forgetMeNot(3):exits and passes control back to main• Main: prints “Love, Me”
1. main2. forgetMeNot(3)3. “I Miss You”4. forgetMeNot(2)5. “I Miss You”6. forgetMeNot(1)7. “I Miss You”8. forgetMeNot(0)9. return10. return11. return12. return13. “Love, Me”
Tracing a Recursive Method
The trace is read from top to bottom as the program executes:
• Indentations are made every time a recursive call is made. • When a method returns, the indentation goes back to the previous level. • Execution continues at the statement following the recursive call.
You can tell at any time:
• which instance of a method is currently active. • which are suspended and waiting.• which are finished running.
Tracing a Recursive Method
• On line 11:
• the method calls forgetMeNot(0) and forgetMeNot(1) are finished• forgetMeNot(2) is running and near completion • forgetMeNot(3) is suspended and waiting for forgetMeNot(2) to return
• Every recursive method must have a way out, i.e., a terminating case. E
• Every recursive method must have a non-recursive option. • This terminating case is sometimes called the base case.
• Without a base case, a recursive method runs forever.
• The sequence of recursive calls must eventually reach the base case.
Tail (Loop) Recursion
• In the trace of forgetMeNot(...) all the returns are stacked diagonally.
• No statements are left to execute after each recursive call returns.
• This kind of recursion is called tail recursion or loop recursion.
• The recursive method executes very much like a loop.
• A tail recursive method makes just one recursive call and exits after the recursive call returns.
• If forgetMeNot(...) included a few additional statements following the recursive call the trace
would be more complicated.
• The computer keeps track of this potential maze of method calls.
• The programmer does not need to think about this at all.
• Solving problems recursively requires a new way of thinking that, unlike iterative programs, does not involve tracing through the execution of the program.
Recursive Thinking
• Assume that you have a best friend who is willing and able to solve the same kind of problem that you are trying to solve.
• Recursion is the process of using your friend to help
you solve your problem • This two-line algorithm is an example of recursive
thinking. To sort a list of n numbers:
• Ask your friend to sort the first n – 1 numbers.• Insert the last number into the appropriate spot in the sorted list of
n – 1 numbers.
Recursive Thinking
• Once the fist n-1 numbers are sorted, the only thing left to do is insert the last value into the list where it belongs.
• The correctness of the algorithm is based on the assumption that your friend did the job correctly in step 1.
• To think recursively, trust that your friend will do the job correctly, and concentrate on using your friend’s effort in the right way.
Recursive Thinking
• Suppose that you are at an ATM and that you must enter a dollar value between 20 and 200.
• The machine continues requesting an amount until you supply a valid value.
Recursive Solution:
To “Accept a deposit between 20 and 200”:
• Accept a value.• If the value is between 20 and 200 then accept it otherwise go on to step (c).• Have your friend “Accept a deposit between 20 and 200”.
Recursive Thinking
• Note that step (3) is a request to your friend to solve the same kind of problem you are trying to solve.
• At this point, you might wonder:
• How does step (3) work? How does my friend know what to do?
• Answer: Your friend follows the same instructions that you follow.
• Isn’t this logic circular? Won’t my friend just have to ask another friend for help?
• Answer: Yes, your friend will call his friend, and his friend will call another friend, and so on. But this is not circular, because at some point a friend is no longer needed. This happens at the base case (line 2), and in this program, the base case occurs when the input value is between 20 and 200.
Recursive Thinking
• Step (3) is a request to your friend to solve the same kind of problem you are trying to solve:
• How does step (3) work? How does my friend know what to do?
• Answer: Your friend follows the same instructions you follow.
• Isn’t this logic circular? Won’t my friend just have to ask another friend for help?
• Answer: Yes, your friend will call his friend, and his friend will call another friend, and so on. But this is not circular, because at some point a friend is no longer needed. This is the base case (line 2) and in this program, the base case occurs when the input value is between 20 and 200.
Recursive Thinking
• This task could also be accomplished with a loop.
Loop Solution:
To “Accept a deposit between 20 and 200”:
• Accept a value;
• While the value is not between 20 and 200
• Accept a value;
• Return the value;
From Recursive Thinking To a Java Method
• Once you have a recursive algorithm, translating your algorithm into a Java method is a mechanical process.
• Every request of your friend becomes a recursive call Problem Statement:
• Write a program using a recursive method that accepts an ATM deposit between 20 and 200 dollars.
Recursive Solution:
• The main method of the following program requests and prints the value obtained by getDeposit() The recursive method getDeposit() on lines 3-13 of the Java solution mimics the algorithm:
• To “Accept a deposit between 20 and 200”• Accept any value.• If the value is between 20 and 200 then return it otherwise go on to step (3).• Have your friend “Accept a deposit between 20 and 200”
From Recursive Thinking To a Java Method
Solution:1. import java.util.*;2. public class Deposit3. {4. public static int getDeposit()5. { 6. int value;7. System.out.println("Please input a value between 20 and 200");8. Scanner input = new Scanner(System.in);9. value = input.nextInt();10. if ((value > 20) && (value < 200)) 11. return value;12. else13. return getDeposit(); // a recursive call14. } 15. public static void main(String[] args)16. { 17. int x = getDeposit();18. System.out.println("Your deposit was " + x + " dollars");19. }20. }
Output
Please input a value between 20 and 200: 130Your deposit was 130 dollars. The output is shown again for the input: 298, 12, 109. Please input a value between 20 and 200: 298Please input a value between 20 and 200: 12Please input a value between 20 and 200:109Your deposit was 109 dollars.
From Recursive Thinking To a Java Method
Discussion:
• getDeposit(), is tail recursive because no actions occur in getDeposit() after the recursive call:
maingetDeposit()“Please input a value between 20 and 200”298
getDeposit()“Please input a value between 20 and 200”12
getDeposit()109return 109
return 109return 109
“Your deposit was 109 dollars”
• Note that getDeposit() returns a value.
• The value 109 is returned by the 3rd call of getDeposit() to the second call of getDeposit(), which returns it to the first call of getDeposit(), and finally to main(...), which prints “Your deposit was 109 dollars.”
• If a recursive method returns a value, do not ignore that value.
• Ignoring a returned value usually indicates a logical error.
Designing Recursive Methods with Parameters
Problem Statement
Suppose that you keep a record of your checkbook transactions, positive for deposits and negative for checks and withdrawals.
Write a program that calculates the sum of the deposits among the first n transactions of a checkbook register.
Designing Recursive Methods with Parameters
Recursive Solution:
To sum the deposits among the first n transactions of your checkbook:
a) If n equals one then handle the task yourself and don’t bother your friend. Return the single value if it is positive, otherwise return 0 (lines 8-12). If n is greater than one, go to steps (b) and (c).
b) Ask your friend to sum the deposits among the first n – 1 transactions in your checkbook, and tell you the answer.
c) If the nth transaction is positive then add that number to what your friend tells you and return the sum, otherwise just return what he tells you.
Designing Recursive Methods with Parameters
Solution: 1. import java.util.*;2. public class AddDeposits3. { 4. public static int addDeposit(int[] checkbook, int numEntries)5. { 6. if (numEntries <= 0) // Just in case the user typed in 0 or fewer transactions to start7. return 0; // This should not normally occur.8. if (numEntries == 1) // The actual base case in a normal execution9. if (checkbook[0]>0) 10. return checkbook[0]; 11. else12. return 0; 13. int sum = addDeposit (checkbook, numEntries - 1); // Here is the recursion (step b)14. if (checkbook[numEntries-1] > 0) 15. return (sum + checkbook[numEntries-1]); 16. else 17. return (sum); // (step c)18. }
Designing Recursive Methods with Parameters
19. public static void main(String[] args) // The main method asks how many transactions
20. // to expect, and accepts that many into an array. 21. { // Transactions are negative integers for withdrawals22. Scanner input = new Scanner(System.in);
// and positive integers for deposits.23. System.out.println("How many transactions?");24. int size = input.nextInt();25. int checkbook[] = new int[size] ;26. for (int i=0; i<size ; i++)27. {28. System.out.println("Input next transaction:");29. checkbook[i]=input.nextInt(); 30. }31. System.out.println("Your deposits add up to " + addDeposit(checkbook,
size) + " dollars.");32. } 33. }
Output
How many transactions?5Input next transaction:10Input next transaction: 20Input next transaction: -15Input next transaction: 30Input next transaction: -10Your deposits add up to 60 dollars.
Designing Recursive Methods with Parameters
A trace:
• Notice the direct correspondence between the Java version and the recursive algorithm.
• The main method:
addDeposit(arr,5)addDeposit(arr, 4)
addDeposit(arr, 3)addDeposit(arr, 2)
addDeposit(arr, 1)return 10
sum = 10return (10 + arr[2]) // return 30
sum = 30return 30
sum = 30return (30 + arr[4]) // return 60
sum = 60return 60
A method that is non-tail recursive
Problem Statement:
• Your school runs a housing lottery for dormitory rooms. Each student is assigned a random 6-digit number, and the student given the lowest number chooses a room first, followed by the student with the next lowest number and so on. These numbers are all stored in an array, and they are announced one by one in order from lowest to highest. When a student’s number is announced, he/she steps up to choose a room. Design and implement a method that returns the lowest integer stored in an array.
A method that is non-tail recursive
Recursive Solution
To find the lowest value in an array:
• If the array has just one value, return that value.• Otherwise, ask your friend to consider an array
identical to yours but with the last value excluded, and find the lowest value in this smaller array.
• Return either the last value in the array or the value your friend returned to you, whichever is smaller.
A method that is non-tail recursive
The recursive method occurs on lines 4-13. 1. import java.util.*;2. public class Lottery3. { 4. public static int findLowest (int arr[], int size)5. // Finds the lowest integer in arr[]from index start to end6. {7. if (size == 1) 8. return arr[0]; // Step (a)9. int temp = findLowest(arr, size-1); // Step (b)10. if (temp < arr[size-1]) 11. return (temp); 12. else return arr[size-1]; // Step (c)13. }
A method that is non-tail recursive
14. public static void main(String[] args) // The main method asks for the number of students // and requests the lottery number for each.
15. { 16. Scanner input = new Scanner(System.in);
17. System.out.println("How many students");18. int size = input.nextInt();19. int a[] = new int[size] ;20. for (int i=0; i<size ; i++)21. {22. System.out.println("Input lottery number:");23. a[i]=input.nextInt(); 24. }25. System.out.println("The lowest lottery number is: " +
findLowest(a, size));26. }27. }
Output
How many students?3Input lottery number:28Input lottery number:32Input lottery number:20The lowest lottery number is: 20
A method that is non-tail recursive
Discussion:
• Lines 15-24: The statements that occur on these lines request the number of students and fill the array with student housing numbers.
• Line 25: The call findLowest(a, size) returns the lowest value in the array.
• Lines 4-13: This is the recursive method, findLowest(int [] arr, int size).
• The trace is shown:
findLowest(a,3)findLowest(a,2)
findLowest(a,1)return 28
temp = 28;if (28 < arr[2-1]) return 28 else return arr[1]Since arr[1]=32, 28 is returnedreturn 28
temp = 28;if(28 < arr[3-1]) return 28 else return arr[2]Since arr[2]= 20, 20 is returnedreturn 20
Methods with More than One Recursive Call
• More than one recursive method call can appear in a recursive method.
• The number of different method calls and the
number of parameters are independent of whether or not the method is tail recursive.
Methods with More than One Recursive Call
1. import java.util.*;2. public class BinarySearch3. {4. public static int binSearch(char[] x, int start, int finish, char key)5. // x is an ascending sorted array of characters6. // key is the character for which we are searching7. // start and finish are the indices that mark the sub array of the array being searched8. // binSearch returns the index in the array that contains key, or (-1) if key is not found.9. { 10. if (start > finish) return -1; // Key is not found, the range has collapsed11. int mid = (start + finish)/2; // mid is half way between the two endpoints12. if (x[mid] == key) // key is found13. return mid; 14. else 15. if (x[mid] < key) // search the upper half16. return binSearch(x, mid+1, finish, key); 17. else18. return binSearch(x, start, mid-1, key); // search lower half19. }
Methods with More than One Recursive Call
20. public static void main(String[] args) //This tests the binSearch method by filling an array
21. { // with the characters ‘A’ through ‘Z’22. char[] a = new char[26] ;23. for (int i=0; i<26 ; i++) // fill the array ‘A’ – ‘Z’24. a[i] = (char)(i+65) ; // 65 represents ‘A’25. System.out.println( “F is in location ” + binSearch(a, 0, 25, ‘F’) +“ of the array”);26. System.out.println(“S is in location ” + binSearch(a, 0, 25, ‘S’) + “ of the array”);27. System.out.println(“Z is in location ” + binSearch(a, 0, 25, ‘Z’) + “ of the array”);28. if (binSearch(a, 0, 25, ‘!’) == -1) 29. System.out.println(“! is not located in the array”);30. }31. }
Output
F is in location 5 of the arrayS is in location 18 of the arrayZ is in location 25 of the array! is not located in the array
Discussion
The main(...) method (lines 20-30) creates a sorted character array with the characters 'A' through 'Z' and tests the binSearch() method with a number of different data.
A trace through the execution of the recursive call on line 26, binSearch(a, 0, 25, ‘S’).
binSearch(a, 0, 25, ‘S’)mid = (0+25)/2 = 12;
a[12] == ‘S’ is false; a[12]< ‘S’) return binSearch(a, 13, 25, ‘S’)
mid = (13+25)/2 = 19; a[19] == ’S’ is false; a[19] > ‘S’
return binSearch(a, 13, 18, ‘S’);mid = (13+18)/2 = 15;
a[15] ==‘S’ is false; a[15] < ‘S’ return binSearch(a, 16, 18, ‘S’);
mid = (16+18)/2 = 17; a[17] == ‘S’ is false; a[17] < ‘S’
return binSearch(a, 18, 18, ‘S’);mid = (18+18)/2 = 18; a[18] ==‘S’
is truereturn 18
return 18return 18
return 18return 18
return 18
The Runtime Stack: Tail Recursion vs. Classic Recursion
• Any method, recursive or not, may have parameters and local variables.
• When a method is called, the JVM allocates or
reserves memory for the parameters and local variables of the method.
• This section of the computer's memory is called the
runtime stack or simply the stack. • When a method exits, the memory allocated to its
parameters and variables is freed or de-allocated for other use
The Runtime Stack: Tail Recursion vs. Classic Recursion
1.public class AddEmUp2.{3. public static int add(int first, int second)4. {5. int sum;6. sum = first + second;7. return sum;8. }9. 10. public static void main(String [] args)11. {12. int num1 = 5;13. int num2 = 6;14. int sum = add(num1, num2);15. System.out.println("The sum is "+ sum);16. }17. 18.} Memory is allocated for a method
The Runtime Stack: Tail Recursion vs. Classic Recursion
public class Recur{ public static void recur(int n) { if (n <= 1) // the base case, stops recursion. <= is safer style but ==
is okay. System.out.println(n+n); else { int sum = n + n; recur(n-1); // a recursive call System.out.println(sum); //executes this upon return } } public static void main(String[] args) { int number = 3; recur(number); System.out.println("All Done!"); } }
• Each time recur() is called, memory for its single parameter and local variable is allocated.
• Each time a recursive call returns to the calling method which then resumes execution, the values of the calling method’s variables and parameters are saved on the stack.
The Runtime Stack: Tail Recursion vs. Classic Recursion
A Trace of recur(3). The stack grows and shrinks.
The Runtime Stack: Tail Recursion vs. Classic Recursion
• The recur(int n) method, which prints the value of sum upon return, a tail recursive method performs no actions after the recursive call returns.
• Upon return of a tail recursive method, no values that have been saved on the stack are accessed.
• Favor iteration over tail recursion.
• It is not always possible to implement an algorithm with tail recursion.
Classic Recursive Algorithms
• Some algorithms intrinsically need to maintain the stack of values.
• These are so-called classic recursive algorithms.
Quicksort - A Classic Recursive Algorithm
• Quicksort is a classic recursive sorting algorithm.
• It is very difficult comprehend this algorithm in terms of iteration.
• Understanding this algorithm requires that you think recursively.
• Sorting routines are perhaps the most common of all algorithms.
• If searching is the workhorse of e-commerce, then sorting is its twin brother.
• Consider the following recursive algorithms: one is a famous classic
recursive algorithm and the other is a recursive version of a well-known algorithm usually implemented iteratively.
Quicksort Algorithm
To sort a list of n numbers:
• Look at the last number in the list and remember it. Call it pivot.
• Partition the list around pivot. That is, reorder the list so that all the numbers smaller than pivot come first, followed by pivot, followed by all the numbers greater than pivot. Note that partitioning the list does not in itself sort the list.
• Ask your friend to sort the list of numbers smaller than pivot, and then sort the list of numbers greater than pivot.
• To sort the list of numbers: 7, 5, 13, 1, 16, 9:
• The pivot is 9.
• After Step 2: 7, 5, 1, 9, 16, 13.
• Note that the list is not sorted yet. It is just partitioned around 9.
• After step 3 the list is sorted: 1, 5, 7, 9, 13, 16.
Quicksort Algorithm
Problem Statement
Write a program that sorts an array of integers using the quicksort algorithm.
Quicksort Algorithm
Solution
The method:
int partition(int [] a, int low, int high)
rearranges the array elements around pivot = a[high]. It reorders thearray so that all the values in the array less than pivot appear to theleft of pivot and all the values greater than pivot appear to its right. The partition algorithm is not recursive but a bit complex.
Quicksort Algorithm
If: x = [9, 5, 7, 2, 0, 3, 8, 4, 1, 6]
a call to partition(x,0,9) rearranges these numbers in x as:
[5, 2, 0, 3, 4, 1, 6, 7, 9, 8].
• Here pivot is 6.
• All values to the left of 6 are less than 6, all values to the right of 6 are greater than 6.
• 6 is in its final position within the soon-to-be sorted array.
• Once 6 has been placed, two smaller sorting problems remain:
sort [5, 2, 0, 3, 4, 1] and sort [7, 9, 8].
Quicksort Algorithm
Assume that:
partition(int [] a, int low, int high) is a black box that places pivot = a[high] into its final position sothat all values to the “left” ofpivot are less than or equal to pivot and all values to the “right”of pivot are greater than pivot.
Quicksort Algorithm
1. public class QuickSort2. {3. public static int partition (int[] a, int low, int high)
// places pivot = a[high] in its final position4. {5. int left=low-1; int temp;6. int pivot=a[high]; 7. for (int right=low; right<high; right++)8. {9. if (a[right] <= pivot)10. {11. left++;12. temp=a[left]; //swap a[left] and a[right]13. a[left]=a[right]; 14. a[right]=temp;15. }16. }17. temp = a[left+1] ; //swap pivot = a[high] with a[left+1]18. a[left+1] = a[high]; 19. a[high] = temp;20. return left+ 1;// pivot's new position in the array21. }
Quicksort Algorithm
22. public static void quickSort (int[] a, int low, int high)23. {24. if (low < high) // if the array has more than one item25. {26. int pivotPlace = partition(a, low, high);
// place pivot into its final position27. quickSort(a, low, pivotPlace-1);
// sort the values left of the pivot28. quickSort(a, pivotPlace+1, high);
// sort the values right of the pivot29. }30. }31. }
Quicksort Algorithm
31. public class QuicksortDemo 32. {33. public static void main(String[] args) 34. {35. int a[] = {7,5,16,1,13,9} ; // sample data36. QuickSort.quickSort(a, 0, 9); 37. System.out.print("The sorted data : ");38. for (int i=0; i<10; i++)39. System.out.print(a[i]+ " ");40. } 41. }42.
Output
The sorted data : [1,5,7,9,13,16]
Quicksort Algorithm
quickSort(…) acting on [7 5 16 1 13 9]