Date post: | 26-Mar-2015 |
Category: |
Documents |
Upload: | arianna-mccarthy |
View: | 217 times |
Download: | 0 times |
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
Chapter 15
Recursive Algorithms
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
Chapter 15 Objectives
After you have read and studied this chapter, you should be able to• Write recursive algorithms for
mathematical functions and nonnumerical operations.
• Decide when to use recursion and when not to.
• Describe the recursive quicksort algorithm and explain how its performance is better than that of selection and bubble sort algorithms.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.1 Basic Elements of Recursion
A recursive method is a method that contains a statement that makes a call to itself.
Any recursive method will include the following three basic elements:• A test to stop or continue the
recursion.• An end case that terminates the
recursion.• A recursive call that continues the
recursion.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.1 Basic Elements of Recursion
These three elements are included in the following recursive factorial method:
public int factorial(int N)
{
if (N == 1){
return 1;
}
else {
return N* factorial(N-1);
}
}
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.2 Directory Listing
Recursive algorithms may be used for nonnumerical applications.
This example of a recursive algorithm will list the file names of all files in a given directory of a hard disk and its subdirectories.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.2 Directory Listing
We create a new File object by passing the name of a file or a directory:
File file = new File(“D:/Java/Projects”);
To get an array of names of files and subdirectories, we use the list method.
String[] fileList = file.list();
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.2 Directory Listing
Following is the complete directoryListing method (the argument of which is a File object that represents a directory):
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.2 Directory Listingpublic void directoryListing(File dir){
String[] fileList = dir.list();
//get the contents
String dirPath = dir.getAbsolutePath();
for (int i=0; i<fileList.length; i++){
File file = new File(dirPath + “/” + fileList[i]);
if(file.isFile()) { //it’s a file
System.out.println(file.getName() );
}else{
directoryListing(file);
//it’s a directory so recurse
}
}
}
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.3 Anagram
We can use a recursive method to derive all anagrams of a given word.
When we find the end case (an anagram), we will print it out.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
Fig. 15.1
How to generate all the anagrams of a word by using recursion.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.3 Anagram
We find the anagrams by rotating the positions of the letters.
The trick is, when the method is called recursively, we pass a word with the first letter cut off. So the words being passed to successive calls are getting shorter and shorter.
However, we must access all letters of the word in order to print it out.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.3 Anagram
We solve this problem by passing two parameters: the prefix and the suffix of a word.
In each successive call, the prefix increases by one letter and the suffix decreases by one letter.
When the suffix becomes one letter only, the recursion stops.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.3 Anagram
public void anagram(String prefix,
String suffix)
{
String newPrefix, newSuffix;
int numOfChars = suffix.length();
if (numOfChars == 1){
//End case: print out one anagram
System.out.println(prefix + suffix);
}else{
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.3 Anagram for (int i = 1; i <= numOfChars; i++){
newSuffix = suffix.substring(1, numOfChars);
newPrefix = prefix + suffix.charAt(0);
anagram(newPrefix, newSuffix);//recursive call
//rotate left to create a //rearranged suffix
suffix = newSuffix + suffix.charAt(0); }}
}
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.4 Towers of Hanoi
The goal of the Towers of Hanoi puzzle is to move N disks from peg 1 to peg 3:
• You must move one disk at a time.• You must never place a larger disk on
top of a smaller disk.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
Fig. 15.2
Towers of Hanoi with N=4 disks.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.4 Towers of Hanoi
This puzzle can be solved effectively using recursion.
The top N-1 disks must be moved to peg 2, allowing you to then move the largest disk from peg 1 to peg 3.
You can then move the N-1 disks from peg 2 to peg 3.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
Fig. 15.3
Recursive solution to the Towers of Hanoi puzzle.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.4 Towers of Hanoipublic void towersOfHanoi (int N,
int from, int to, int spare){if (N == 1){
moveOne(from, to);}else{
towersOfHanoi(N-1, from, spare, to);moveOne (from, to);towersOfHanoi(N-1, spare, to, from);
}}
private void moveOne(int from, int to){System.out.println(from + “--” + to);
}
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.5 Quicksort
The quicksort sorting algorithm uses recursion.
To sort an array from index low to high, we first select a pivot element p.
Any element may be used for the pivot, but for this example we will user number[low].
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
Fig. 15.4
The core idea of the quicksort algorithm.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.5 Quicksort
Using p as the pivot, we scan through the array. We move all elements smaller than p to the lower half and all elements larger than p to the upper half.
Then we sort the lower and upper halves recursively, using quicksort.
The variable mid points to the position where the pivot is placed.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.5 Quicksort
Here is the quicksort algorithm:public void quickSort(int[] number,
int low, int high){
if (low < high){ int mid = partition(number, low, high);
quickSort(number, low, mid-1); quickSort(number, mid+1, high);}
}
The partition method splits the array elements number[low] to number[high] into two halves.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
Fig. 15.5
Result of partitioning using 23 as a pivot.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.5 Quicksort
We first set the pivot to number[low].
We then look for a number smaller than the pivot from position high, high-1, and so on.
The number is found at position J. We move this number to position low.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.5 Quicksort
Now we look for a number larger than the pivot from low+1, low+2, etc.
The number is found at position I. We move it to position J.
We repeat the process, looking for a number smaller than the pivot from J-1, J-2, and so on.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
Fig. 15.6
Details of one partitioning.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.5 Quicksort
Here is the partition method:private int partition(int[] number,
int start, int end){
//set the pivot
int pivot = number[start];
do{
//look for a number smaller than pivot
//from the end
while(start < end && number[end] >= pivot){
end--;
}
if(start < end){ //found a smaller number
number[start] = number[end];
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.5 Quicksort
//now find a number larger than pivot //from the start while(start < end && number[start] <= pivot){
start++; } if(start < end){ //found a larger number
number[end] = number[start]; }}
} while (start < end);
//done, move the pivot back to the arraynumber[start] = pivot;
return start;}
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.5 Quicksort
In the worst case, quicksort executes roughly the same number of comparisons as the selection sort and bubble sort.
On average, we can expect a partition process to split the array into two roughly equal subarrays.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
Fig. 15.7
A hierarchy of partitioning an array into smaller and smaller arrays in the quicksort.
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.5 Quicksort
When the size of all subarrays becomes 1, the array is sorted.
The total number of comparisons for sorting the whole array is
K * NN = 2K
log2N = K
KN = N log2N
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.6 When Not to Use Recursion
Recursion does not always provide an efficient or natural way to express the solution to a problem.
public int fibonacci(int N){
if (N == 0 )|| N ==1){
return 1; //end case
}else{ //recursive case
return fibonacci(N-1) + fibonacci(N-2);
}
}
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
Fig. 15.8
Recursive calls to compute fibonacci(5).
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.6 When Not to Use Recursion
This method is succinct and easy to understand, but it is very inefficient. A nonrecursive version is just as easy to understand.
public int fibonacci(int N){
int fibN, fibN1, fibN2, cnt;
if (N == 0 || N == 1){
return 1;
}else{
fibN1 = fibN2 = 1;
cnt = 2;
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.6 When Not to Use Recursion while (cnt <= N){
fibN = fibN1 + fibN2;
//get the next Fib. no.
fibN1 = fibN2;fibN2 = fibN;
cnt++; } return fibN; }}
©TheMcGraw-Hill Companies, Inc. Permission required for reproduction or display.
15.6 When Not to Use Recursion
In general, use recursion if• A recursive solution is natural and
easy to understand.• A recursive solution does not result in
excessive duplicate computation.• The equivalent iterative solution is
too complex.