Exercises from Chapter 7 7.4 7.9 7.12 7.15 7.16 7.17 7.19 Solutions 7.4 It will reduce the number of comparisons from O(n^2) in the worst case to O(n log n) in the worst case. But it will stil require shifting elements in the array to the right in order to insert the element. So the asymptotic running time will be the same. 7.9 (a) Each call to qsort costs \Theta(i log i) on average. So the total average cost is sum_{i=1}^{n} \Theta(i log i) We can simplify this sum as follows: sum_{i=1}^{n} i log i < sum_{i=1}^{n} n log n = n^2 log n and sum_{i=1}^{n} i log i > sum_{i=n/2}^{n} n/2 log(n/2) = \Omega(n^2 log n) so sum_{i=1}^{n} \Theta(i log i) = \Theta(n^2 log n) (b) Each call to qsort costs \Theta(n log n). So the total cost is \Theta(n^2 log n) 7.12 (a) The worst case happens when all of the sublists are of size 1, except for one list of size n-k+1. When SORTk is called recursively on the large sublist, the worst case again occurs if one of the sublists has size n-2(k-1), and so on. If this happens on each call to SPLITk, we can use a recurrence to estimate the worst case run time: T(1) = 1 T(n) = n + T(n-k). Unwinding the recurrence, and assuming that n = (n/k)k exactly, T(n) = n + n-k + n-2k + n-3k + ... + 0 Therefore T(n) = n^2 -((n/k)(n/k + 1)/2)k = \Theta(n^2). (b) In the average case the sublists will be of nearly equal size. That is, the first time they will be of size n/k, the next time n/k^2, etc. That is, the depth of recursion will be log_k n. This time the recurrence is: T(1) = 1 T(n) = n + kT(n/k). Unwinding the recurrence, and assuming that n = k^d exactly for some d, T(n) = k^d + dn = n + nlog_k(n) = \Theta(nlog n). 7.15 Since there are no duplicates, a version of binsort will be very fast, and each bin needs only a single bit of memory, which indicates whether or not the corresponding value occurs in the list. The algorithm begins by creating an array B of 30,000 bits, all initialized to 0. Then it scans the list of numbers. For each number i, it changes B[i] to 1. Then it scans B. If B[i] = 1, then it writes the value i back to the next position in the list. 7.16 (a) \Theta(n) worst case time without sorting. (b) \Theta(n) worst case time without sorting. (c) \Theta(n) worst case time without sorting. (d) \Theta(n log n) with sorting followed by selecting the middle element. (e) \Theta(n log n) with sorting followed by scanning the array for the most frequent value. 7.17 Every invocation of Mergesort will result in either two calls to Mergesort or a call to Insertion Sort. Thus the calls can be arranged into a full binary tree, where the leaves are those calls to Mergesort that result in a call to Insertion Sort and the internal nodes are those calls to Mergesort that result in two recursive calls to Mergesort. By the Full Binary Tree Theorem, if there are n calls to Mergesort, there will be (n+1)/2 calls, or about n/2, calls to Insertion Sort. 7.19 Consider a decision tree for a search algorithm that uses comparisons to find an element in an array of length n. The internal nodes of this tree are labeled with comparisons, and the leaves are labeled with some number 0 to n-1, indicating the position where the element was found. Since the element can be in any of these n positions, the tree has at least n leaves. Therefore its height is at least log n, by Lemma 2 in class. Therefore, by the same argument as in Lemma 1, it has worst-case run time of Omega(log n).