Fundamentals of Java Chapter 13: Recursion, Complexity, and Searching and Sorting UNIT 3— ARRAYS, RECURSION, AND COMPLEXITY CHAPTER 13— RECURSION, COMPLEXITY, AND SEARCHING AND SORTING EXERCISE 13.1 1. The presence of a well-defined stopping state keeps a recursive definition from being circular. The stopping state is a test of a condition and an action that does not result in a recursive step. 2. The two parts of any recursive method are a stopping state and a recursive step. 3. Recursion in most programming languages requires a call stack to track the data for each call of a recursive method. Each time a recursive method is called, the computer allocates memory for the method’s information on the stack and copies the information into this memory. Thus, there is a cost of memory and time as well. 4. The primary benefit of using recursion is that the programmer can write solutions to some problems more easily and with less effort than solutions that use a loop. Some problems lend themselves quite naturally to recursive solutions and not at all naturally to iterative ones. 5. raise(2, 5) raise(2, 4) raise(2, 3) raise(2, 2) raise(2, 1) raise(2, 0) returns 1 returns 2 returns 4 returns 8 returns 16 returns 32 6. The method whatAMethod is always called recursively with the same value, n. Thus, the stopping state, when n == 0, is never reached. The computer tolerates these recursive calls until there is no more stack space and then throws an exception. EXERCISE 13.2 1. a. The run time of factorial is O(n). b. The run time of raise is O(n). 2. a. The memory usage of factorial is O(n). b. The memory usage of fibonacci is O(n). 3. The run time of the sort method is O(n2). 1 Fundamentals of Java Chapter 13: Recursion, Complexity, and Searching and Sorting EXERCISE 13.3 1. int raise(int base, int expo){ if (expo == 0) return 1; else if (expo % 2 == 0){ int result = raise(base, expo / 2); return result * result; }else return base * expo(base, expo – 1); } 2. raise(2, 16) raise(2, 8) raise(2, 4) raise(2, 2) raise(2, 1) raise(2, 0) returns 1 returns 2 returns 4 returns 16 returns 256 returns 65536 3. For large n, raise tends to divide n by 2 on each recursive call, so the method is O(logn). EXERCISE 13.4 1. The strategy of quick sort is to select a pivot item and shift all of the smaller items to its left and all of the larger items to its right. This part of the process is linear. If the pivot item tends to be in the middle of the list, the number of times the shifting must occur is logarithmic, so that’s why quicksort can be better than O(n2). 2. Quicksort is not O(nlogn) in all cases because the pivot item might be near the beginning or the end of the list, thus causing an uneven split of the list. If this situation occurs often enough, the run time of quicksort can degenerate to O(n2) in the worst case. 3. Three methods to select a pivot value are to pick the item at the midpoint, to pick an item at a random position, and to pick the median of the first, last, and middle items. 4. If the subarray is relatively small, running an insertion sort might be a bright idea for two reasons. First, this algorithm probably performs just as well as a quicksort on small data sets. Second, the behavior of insertion sort is close to linear when the array is partially sorted, as many subarrays would be within the context of a quicksort. EXERCISE 13.6 2 Fundamentals of Java Chapter 13: Recursion, Complexity, and Searching and Sorting 1. There are 7, 31, and 511 total calls of the cCurve for levels 2, 4, and 8, respectively. There are exactly 2(n + 1) – 1 total calls for level n. Therefore, the complexity of cCurve is approximately O(2n). 2. The method would flip a coin by selecting a random number between 0 and 1 and choose right or left or top or bottom based on the number chosen. REVIEW QUESTIONS Fill in the Blank 1. 2. 3. 4. 5. 6. 7. 8. 9. stopping state recursive step call stack activation record infinite recursion stack overflow O(n), O(n2), and O(logn) O(n2), O(n) O(nlogn), O(n2) 3