1. The sequence we want to sort is:
      3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5
      
      3, 9, and 5 are the three values selected as potential pivot candidates. 3 and 5 are the left and rightmost elements in the array and 9 is the center element:
             a[center] = a[(left + right)/2] = a[(0 + 10)/2] = a[5] = 9
             
      3 and 9 are the first two values compared because they are the left and center values. They are in the proper order so we proceed to the next step, where the left and right values are compared, which are 3 and 5. Again the values are in the correct order. The final test for determining the pivot is to compare the current center and right values, which are 9 and 5. Since 5 is less than 9, 5 and 9 swap positions:
             
           3, 1, 4, 1, 5, 5, 2, 6, 5, 3, 9
             
      Next we place the pivot aside by swapping it with the next to last element in the array, which is 3:
             
           3, 1, 4, 1, 5, 3, 2, 6, 5, 5, 9
             
      The leftmost and rightmost elements, 3 and 9 respectively, are now in the correct partition and 5 has been set aside until the end. Hence we set our left index to 1 and our right index to the first element to the left of our pivot. Our initial configuration is then: -----> 3, 1, 4, 1, 5, 3, 2, 6, 5, 5, 9 ^ ^ left right left moves right until it encounters 5, which is equal to the pivot, and hence belongs in the right partition. right stops immediately because it also points to a 5. This 5 belongs in the left partition: -----> 3, 1, 4, 1, 5, 3, 2, 6, 5, 5, 9 ^ ^ left right Now we swap the two 5's and update our indices by moving left one position to the right and right one position to the left: -----> 3, 1, 4, 1, 5, 3, 2, 6, 5, 5, 9 ^ ^ left right left moves rightward until it encounters 6, which is greater than the pivot and thus belongs in the right partition. right moves leftward until it encounters 2, which is less than the pivot and thus belongs in the left partition. -----> 3, 1, 4, 1, 5, 3, 2, 6, 5, 5, 9 ^ ^ right left Since the left and right indices have crossed over all the array elements are now in the correct partition except for the pivot. Note that we do not have to exchange the elements pointed to by left and right because they are already in the correct partition. Our last step is to swap the pivot with the element pointed at by left, which is 6. By doing so we place the pivot in the correct location in the final sorted array: -----> 3, 1, 4, 1, 5, 3, 2, 5, 5, 6, 9 ^ ^ right left This action completes the first partition phase. Note that one 5 appears in the left partition and one 5 appears in the right partition. This is ok because eventually the partitioning process will move the two 5's towards one another until they rest next to the pivot, which is also 5.

    2. The left partition starts at 0 and ends at 6 and the right partition starts at 8 and ends at 10. Note that the left partition ends immediately before the left pointer and the right partition starts immediately after the left pointer. That is because the left pointer points to the pivot element, which belongs to neither partition.

    3. Insertion Sort:
      Initial configuration3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5
      Step 1: swap 1 & 31, 3, 4, 1, 5, 9, 2, 6, 5, 3, 5
      Step 2: 4 is in the right place1, 3, 4, 1, 5, 9, 2, 6, 5, 3, 5
      Step 3: insert 1 after the first 11, 1, 3, 4, 5, 9, 2, 6, 5, 3, 5
      Step 4: 5 is in the right place1, 1, 3, 4, 5, 9, 2, 6, 5, 3, 5
      Step 5: 9 is in the right place1, 1, 3, 4, 5, 9, 2, 6, 5, 3, 5
      Final configuration after 5 steps1, 1, 3, 4, 5, 9, 2, 6, 5, 3, 5

  1. In the following solutions I use the pipe character, |, to separate runs and I use the "$$" string to rexmpsent a sentinel value. I've used an asterisk, *, to indicate elements in the priority queue that belong in the next run:

    Contents of Priority Queue Output Next Element Read p[1] p[2] p[3] p[4] ---------------------------------------------------------------- Run 1 12 42 53 65 12 17 17 42 53 65 17 3 42 53 65 3* 42 81 53 65 81 3* 53 9 65 81 3* 9* 65 98 81 98 3* 9* 81 1 98 1* 3* 9* 98 20 1* 3* 9* 20* End of Run ---------------------------------------------------------------- Run 2 1 3 9 20 1 15 3 9 15 20 3 18 9 15 18 20 9 66 15 18 20 66 15 23 18 20 23 66 18 71 20 23 66 71 20 14 23 66 71 14* 23 43 43 66 71 14* 43 58 58 66 71 14* 58 16 66 71 14* 16* 66 10 71 10* 14* 16* 71 75 75 10* 14* 16* 75 End of Input 10* 14* 16* End of Run ---------------------------------------------------------------- Run 3 10 14 16 10 -- 14 16 14 -- 16 16 -- End of Run D1: 12 17 42 53 65 81 98 $$ 10 14 16 $$ D2: 1 3 9 15 18 20 23 43 58 66 71 75 $$
  2. In an array with all duplicate values every element is equal to the pivot and hence every pair of elements pointed to by left and right will get swapped. Since every pair of elements gets swapped, the left and right partitions will have the same number of elements. Since the partitioning algorithm will then be applied to these two equal-sized partitions, the recurrence relation will be: T(N) = N + 2T(N/2) The initial N term represents the work required to partition the array into two halves. Each element of the array is examined, and hence there is O(N) work required to partition the array. The term T(N/2) represents the time quicksort requires to sort an array of size N/2 and since quicksort will be called recursively on the left and right partitions, we multiply T(N/2) by 2. The solution to this recurrence is N log N and hence the running time of the quicksort algorithm using median-of-three partitioning on an array of duplicate keys is O(N log N).

  3. You know that the final structure of the array is going to have all the false values in the left side of the array, all of the true values in the right side of the array, and all of the maybe values in the middle of the array. An efficient algorithm can take advantage of this information by keeping two indices that point to the two ends of the array and swapping false or true values to either the left or right end of the array when one of those two values is encountered. The maybe values will end up getting shepharded towards the middle of the array. The pseudo-code for this algorithm follows: swap(array a, int x, int y) temp = a[x] a[x] = a[y] a[y] = temp sort(array a, int left, int right) // from the left side of the array, move right until you find the first non-left element left_side = left while (left_side < right) and (a[left_side] = false) left_side++ // from the right side of the array, move left until you find the first non-right element right_side = right while (right_side > left) and (a[right_side] = true) right_side-- index = left_side while (index <= right_side) // swap false elements to the left side of the array // and true elements to the right side of the array. // maybe elements will naturally get shepharded into the center if a[index] = 'false' swap(a, left_side, index) left_side++ index++ // in case of a true element do not increment the index because you cannot // be sure what value gets swapped into the index location from the rightside // location else if a[index] = 'true' swap(a, right_side, index) right_side-- else index++ // do nothing with a 'maybe' element