3.5.3
1. Given the list of elements, [23, 17, 21, 3, 42, 9, 13, 1, 2, 7, 35, 4], what would be their order after the loop for the heap construction phase exe- cutes?
2. Given the list of elements, [3, 9, 14, 12, 2, 17, 15, 8, 6, 18, 20, 1], what would be their order after the loop for the heap construction phase exe- cutes?
3. We could shorten the second for loop in our heapsort by changing the ending condition to i ≥ 3. What, if anything, would need to be added after thisfor loop to assure that the final list is sorted? Would this change reduce the number of comparisons (give a detailed reason for your answer)?
4. Prove that a list in reverse order is a heap.
last the index of the last element in the part of list to sort if first < last then
middle = ( first + last ) / 2 MergeSort( list, first, middle ) MergeSort( list, middle + 1, last )
MergeLists( list, first, middle, middle + 1, last ) end if
It should be obvious that the work is all being done in the function Merge- Lists. We now will develop MergeLists.
Consider lists A and B, both sorted in increasing order. This ordering means that the smallest element of each list is in the first location, and the largest ele- ment of each list is in the last location. To merge these together into one list, we know that the smallest element overall must be either the first element of A or the first element of B, and the largest element overall must be either the last element of A or the last element of B. If we want to create a new list C that is the sorted combination of A and B, we will begin by moving the smaller of A[1] and B[1] into C[1]. But what gets moved into C[2]? If A[1] was smaller than B[1], A[1] was moved into C[1], and the next element might be B[1]
unless A[2] is also smaller than B[1]. This is possible because all we really know is that A[2] is larger than A[1] and smaller than A[3], but we don’t know how the elements of A relate in size to the elements of B. It seems that the best way to accomplish the merge would be to have two indices for A and B and incre- ment the index for the list that has the smaller element. The general process keeps comparing the smallest elements of what is left of lists A and B and moves the smaller of these two into C. At some point, however, we will “run out” of elements in either list A or B. The elements “left over” will be those in one list that are greater than the last element of the other list. We need to make sure that these elements are moved to the end of the result list.
Putting these ideas together into an algorithm gives us
MergeLists( list, start1, end1, start2, end2 ) list the elements to be put into order start1 beginning of “list” A
end1 end of “list” A start2 beginning of “list” B end2 end of “list” B
// assumes that the elements of A and B are contiguous in list finalStart = start1
finalEnd = end2 indexC = 1
while (start1 ≤ end1) and (start2 ≤ end2) do if list[start1] < list[start2] then result[indexC] = list[start1]
start1 = start1 + 1 else
result[indexC] = list[start2]
start2 = start2 + 1 end if
indexC = indexC + 1 end while
// move the part of the list that is left over if (start1 ≤ end1) then
for i = start1 to end1 do result[indexC] = list[i]
indexC = indexC + 1 end for
else
for i = start2 to end2 do result[indexC] = list[i]
indexC = indexC + 1 end for
end if
// now put the result back into the list indexC = 1
for i = finalStart to finalEnd do list[i] = result[indexC]
indexC = indexC + 1 end for
■ 3.6.1 MergeLists Analysis
Because all of the element comparisons occur in MergeLists, we begin ana- lyzing there. Let’s look at the case where all of the elements of list A are smaller than the first element of list B. What will happen in MergeLists? We will begin by comparing A[1] and B[1] and because A[1] is smaller we will move it to C. We then compare A[2] with B[1] and move A[2] because it is smaller.
This process will continue comparing each element of A with B[1], because they are all smaller. This means that the algorithm does NA comparisons, where
NA is the number of elements in list A. Notice that if all of the elements of list B are smaller than the first element of A, the resulting number of comparisons would be NB, where NB is the number of elements in list B.
What if the first element of A is greater than the first element of B but all of the elements of A are smaller than the second element of B? We would com- pare A[1] and B[1] and move B[1] to C. We now find ourselves in the same position we were in the last case, where we will compare every element of A with B[2] as they are moved to the result. This time, however, we not only have done NA comparisons of the elements of A with B[2], but we also did a comparison of A[1] and B[1], so the total number of comparisons in this case is NA + 1. If we consider other arrangements, we start to see that the case pre- sented in the first paragraph of this subsection might be the best case, and it is.
We saw that if all the elements of list A were between B[1] and B[2], we did more comparisons than if all of the elements of A were smaller than all of the elements of B. Let’s see if taking this to the extreme gives the worst case. Con- sider what happens if the elements of A and B are “interleaved” based on their value. In other words, what happens if the value of A[1] is between B[1] and B[2], the value of A[2] is between B[2] and B[3], the value of A[3] is between B[3] and B[4], and so on. Notice that each comparison moves one element from either A or B into list C. Based on the example ordering above, we move an element of B, then one of A, then one of B, then one of A, until we have moved all but the last element of A. Because the comparisons resulted in mov- ing all but the last element of A, we will have done NA + NB 1 comparisons in this worst case.
■ 3.6.2 MergeSort Analysis
Now that we know the range of complexity of MergeLists, we can now look at MergeSort. Based on the techniques of Section 1.5, we look at the parts of the MergeSort algorithm. First, we notice that the function is called recursively as long as first is less than last. This means that if they are equal or if first is greater than last, there is no recursive call. If first is equal to last, this represents a list of size 1. If first is greater than last, this repre- sents a list of size 0. In both of these cases, the algorithm does nothing, so the direct solution has zero comparisons.
The division of the list into two parts is done by the calculation of middle. We see this calculation is done without any comparisons, so the division step is
zero. Because middle is calculated to be exactly between first and last, we see that we are breaking the list into two sublists, and each one is half the size of the original. If the list has N elements, we create two sublists with N / 2 elements. Based on the analysis of MergeLists, this means that the combine step will take N / 2 comparisons in the best case, and N / 2 + N / 2 1, or N 1, comparisons in the worst case. Putting all of this together gives the two recurrence relations for the worst (W) and best (B) cases.
We now apply the techniques of Section 1.6 to solve these recurrence rela- tions. First, we solve the worst case:
Now we substitute:
We see that the coefficient of W increases at the same rate as the denominator increases. Eventually, this term will become W(1), which has a value of zero, and so this first term will eventually disappear. Notice that each substitution pro- duced another addition of N and the subtraction of the next higher power of 2.
How many of these will be included? We see in the last equation that we have W N( ) = 2W N( ⁄2)+N–1
W( )0 = W( )1 = 0 B N( ) = 2B N( ⁄2)+N⁄2
B( )0 = B( )1 = 0
W N( ⁄2) = 2W N( ⁄4)+N⁄2–1 W N( ⁄4) = 2W N( ⁄8)+N⁄4–1 W N( ⁄8) = 2W N( ⁄16)+N⁄8–1 W N( ⁄16) = 2W N( ⁄32)+N⁄16–1
W N( ) = 2W N( ⁄2)+N–1
W N( ) = 2 2W N( ( ⁄4)+N⁄2–1)+N–1 W N( ) = 4W N( ⁄4)+N–2+N–1
W N( ) = 4 2W N( ( ⁄8)+N⁄4–1)+N–2+N–1 W N( ) = 8W N( ⁄8)+N–4+N–2+N–1
W N( ) = 8 2W N( ( ⁄16)+N⁄8–1)+N–4+N–2+N–1 W N( ) = 16W N( ⁄16)+N–8+N–4+N–2+N–1
W N( ) = 16 2W N( ( ⁄32)+N⁄16–1)+N–8+N–4+N–2+N–1 W N( ) = 32W N( ⁄32)+N–16+N–8+N–4+N–2+N–1
five N terms, the sum of the powers of 2 from 0 (1 = 20) through 4 (16 = 24) and W(N / 32) = W(N / 25). This means that when we get to the point of having W(N / 2lgN) = W(N / N), the closed form of this equation becomes
This means that W(N) = O(N lg N). When we look at W(N) and B(N), we see the difference between the two is that N becomes N / 2. When we look at the role that N played during our substitutions, the reader should see that B(N)⬇ (N lg N) / 2, so it is also the case that B(N) = O(N lg N).
This means that MergeSort is a very efficient sort, even in the worst case, but the problem is that the MergeList function needs extra space to accom- plish the merge.
3.6.3
1. Show the results of each pass of MergeSort applied to the list [7, 3, 9, 4, 2, 5, 6, 1, 8].
2. Show the results of each pass of MergeSort applied to the list [3, 5, 2, 9, 8, 1, 6, 4, 7].
3. In the discussion of MergeLists, it was mentioned that the best case is when all of the values of list A are smaller than the values of list B. This, however, doesn’t say anything about the operation of the entire algorithm for any initial input of values. Exactly how many key comparisons will be done by MergeSort on the list [1, 2, 3, 4, 5, 6, 7, 8]? In general, how many comparisons are done for a list of N elements that is already in increasing order? Show details of all work.
4. Exactly how many key comparisons will be done by MergeSort on the list [8, 7, 6, 5, 4, 3, 2, 1]? In general, how many comparisons are done for a list ofN elements that is in decreasing order? Show details of all work.
5. Create an ordering of the numbers 1 through 8 that will cause MergeSort to do the worst-case number of comparisons of 17. (Hint: Work backward through the sorting process.)
W N( ) N*W( )1 NlgN 2i
i=0 lgN–1
∑
– +
=
W N( ) = NlgN–(2lgN–1) W N( ) = NlgN–N+1
3.6.3 EXERCISES
■