Lecture 15. Mergesort. 10/29/97.

==================================================================================

NOTE: Please add two omitted lines to the pseudocode for mergesort in the programming assignment:

mergesort(first, last) 

if (first < last) then

mergesort(first half of list)

mergesort(second half of list)

merge the two sorted sublists

==================================================================================

15.1. Mergesort is efficient with respect to time. Like quicksort, mergesort is an example of a divide and conquer algorithm. Unlike quicksort, however, mergesort always splits the list remaining to be sorted into two equal pieces. So in all cases the time T(n) to sort an n-element array using mergesort satisfies the recurrence relation

T(n) = 2 T ( |_ n/2 _| ) + cn, T(1) = d, for some constants c and d,

and we have seen that this recurrence relation has a closed form T(n) = "capital theta" (nlog2n). So mergesort always sorts an array of n keys in "capital theta"(nlog2n) time and therefore achieves the same asymptotic time performance as heapsort.

15.2. Mergesort is not efficient with respect to space. Unlike all other sorts we have examined so far, mergesort is not an "in-place" sort, since mergesort uses an auxiliary array C of n elements into which the two subarrays of n/2 elements each are merged. Thus mergesort makes inefficient use of space, requiring "capital theta"(n) extra memory locations to mergesort a list of n elements.

Exercise 15.1. Compare the space requirements of mergesort and quicksort (in the worst case) for sorting an array of n elements.

Exercise 15.2. Suppose mergesort is being used to sort a list of n = 1024 integers.

a. How many times will the procedure mergesort(a, b) be called? (Assume the first call is to mergesort(0,1023).

b. What is the TOTAL number of memory cells which will be used by mergesort in this case (assume one cell holds one integer)?

15.3. Why study mergesort? Even though mergesort is inefficient with respect to space usage, it suggests a sorting technique which can be useful in special situations:

A. in the case where we already have two sorted arrays, of sizes m1 and m2, the "merge" procedure from the programming assignment can be generalized to give a procedure to combine these smaller arrays into one sorted array of m1 + m2 elements.

B. The technique used in mergesort forms the basis for an efficient sorting algorithm in the case of the "external sorting" problem, where the data set to be sorted is too large to fit in main memory. We will see examples of this in Lecture 18.

15.4. Merging lists--a lower bound result. Recall that earlier we showed that any algorithm which correctly searches an n-element list for an element X must do at least

|_ log2n _| comparisons in the worst case. As another example of a lower bound argument, we can prove the following:

Theorem. Any algorithm which correctly merges two sorted n-element lists into one sorted 2n-element list by comparisons of keys must do at least 2n - 1 comparisons in the worst case.

Proof. Suppose we are merging A[0], A[1], . . . , A[n-1] with B[0], B[1], . . . , B[n-1]. Then we can show that the algorithm MUST compare

(a) A[j] with B[j] for 0 <= j <= n-1 and

(b) A[j] with B[j+1] for 0 <= j <= n-2

For suppose there is some j for which the algorithm never compares A[j] with B[j]. Then we can fool the algorithm into making a wrong choice by giving it two lists differing only in the correct ordering of these two elements:

(a1) B[0] < A[0] < B[1] < A[1] < . . . < B[j-1] < A[j-1] < B[j] < A[j] < B[j+1] < A[j+1] < . . . < B[n-1] < A[n-1]

(a2) B[0] < A[0] < B[1] < A[1] < . . . < B[j-1] < A[j-1] < A[j] < B[j] < B[j+1] < A[j+1] < . . . < B[n-1] < A[n-1]

Without comparing A[j] with B[j], the algorithm cannot merge the lists correctly in both of these cases. So it must be that the algorithm compares A[j] with B[j] for each j with 0 <= j <= n-1.

Similarly, if there is some value of j, 0 <= j <= n-2, for which the algorithm does not compare A[j] with B[j+1], we can fool the algorithm into making a wrong choice be giving it two lists

(b1) B[0] < A[0] < B[1] < A[1] < . . . < B[j] < A[j] < B[j+1] < A[j+1] < . . . < B[n-1] < A[n-1]

(b2) B[0] < A[0] < B[1] < A[1] < . . . < B[j] < B[j+1] < A[j] < A[j+1] < . . . < B[n-1] < A[n-1]

and the algorithm cannot merge the lists correctly in both these cases unless it compares A[j] and B[j+1].

Therefore we have shown that merging two n-element lists by comparisons requires at least 2n-1 comparisons in the worst case, i.e., the number of comparisons is "capital omega"(2n-1).

Exercise 15.3. Show that the merge procedure in the programming assignment does 2n-1 comparisons in the worst case, i.e., that this is an optimal merging procedure.

Exercise 15.4. How many comparisons are done during the merge procedure if the keys were in order to start with, i.e., A[0] < A[1] < . . . < A[n-1] < B[0] < B[1] < . . . < B[n-1]?