quickSort, mergeSort, insertionSort

On deck: Three sorting algorithms





heapSort summary: You can sort with heap operations.
  1. phase 1: unsorted to heap. This can be done in O(n) time with the bottom up strategy.
  2. phase 2: heap to sorted. This can be done in O(n log(n)) time. It is essentially just n heap remove() operations.








mergeSort is described in ODS Chapter 11.1.1.
It is implemented in udods/Algorithms.h.

Illustration of mergeSort: detail from example in text.

  13, 8, 5, 2, 4, 0, 6    // a0  from text
  /  /  /    \  \  \  \   
 13  8  5                 // a00
 /    \  \
13                        // a000 - done
       8  5               // a001
      /    \             
     8                    // a0010 - done
            5             // a0011 - done
       5  8               // a001 - merge of a0010 and a0011
  5  8 13                 // a00  - merge of a000 and a001
            2, 4, 0, 6    // a01
           /  /    \  \
          2  4            // a010
         /    \
         2                // a0100 - done         
               4          // a0101 - done         
          2  4            // a010 - merge of a0100 and a0101
                    0, 6  // a011
                   /    \
                  0       // a0110 - done         
                        6 // a0111 - done         
                    0, 6  // a011 - merge of a0110 and a0111
            0, 2, 4, 6    // a01 - merge of a010 and a011
  0, 2, 4, 5, 6, 8, 13    // a0 - merge of a00 and a01
  0, 2, 4, 5, 6, 8, 13    // a is merge of this and 1,3,7,9,10,11,12


Illustration of mergeSort with added base case for n == 2.
  13, 8, 5, 2, 4, 0, 6    // a0  from text
  /  /  /    \  \  \  \   
 13  8  5                 // a00
 /    \  \
13                        // a000 - done
       8  5               // a001
       5  8               // a001 - done (with swap)
  5  8 13                 // a00  - merge of a000 and a001
            2, 4, 0, 6    // a01
           /  /    \  \
          2  4            // a010
          2  4            // a010 - done with no swap
                    0, 6  // a011
                    0, 6  // a011 - done with no swap
            0, 2, 4, 6    // a01 - merge of a010 and a011
  0, 2, 4, 5, 6, 8, 13    // a0 - merge of a00 and a01
  0, 2, 4, 5, 6, 8, 13    // a is merge of this and 1,3,7,9,10,11,12


Alternate merge function:
template<class T>
void merge(array<T> &a0, array<T> &a1, array<T> &a) {
    int i = 0, i0 = 0, i1 = 0;
l1: while (i0 < a0.length and i1 < a1.length) 
        if (compare(a0[i0], a1[i1]) < 0)
            a[i++] = a0[i0++];
        else
            a[i++] = a1[i1++];
l2: while (i0 < a0.length)
        a[i++] = a0[i0++];
l3: while (i1 < a1.length)
        a[i++] = a1[i1++];
}
Notes:
  1. Only one of l2 and l3 will be executed.
  2. Typically l2 or l3 will copy a small number of items.
  3. Block copy could be used (memcopy).



quickSort is described in ODS Chapter 11.1.2.
It is implemented in udods/Algorithms.h. (but note we will do it a bit differently.)


template 
int partition(T *a, int n);

template <typename T>
void quickSort(T *a, int n) { // sort a[0..n-1) into increasing order.
	if (n < 2) return;

	// place random entry (call it "pivot") in first position
	swap(a[0], a[rand()%n]);
	// partition based on that entry
	int i = partition(a, n);
    // now a[0..i-1] are <= pivot and a[i..n-1] are >= pivot

	quickSort(a, i); // sort the i smaller elts.
	quickSort(a+i, n-i); // sort the n-i larger elts.
}

template <typename T>
int partition(T *a, int n){
	int i = 0; j = n-1;
    // pivot is a[i]
	while (i < j) {
	  while (compare(a[i],a[j] < 0)  --j; 
	  swap( a[i++], a[j] ); // now pivot is a[j]
      if (i == j) break;
	  while (compare(a[i],a[j]) < 0) ++i;
	  swap( a[i], a[j--] ); // now pivot is back to a[i]
	}
	return i; // location of pivot.
}
Illlustration of partition, a from text, n = 14
  13, 8, 5, 2, 4, 0, 6, 9, 7, 3,12, 1,10,11    
   *
   9, 8, 5, 2, 4, 0, 6,13, 7, 3,12, 1,10,11    // random swap
partition(a,14)
   -                                *  +  +
   1, 8, 5, 2, 4, 0, 6,13, 7, 3,12, 9,10,11    // 1st swap
   -  -  -  -  -  -  -  *           +  +  +
   1, 8, 5, 2, 4, 0, 6, 9, 7, 3,12,13,10,11    // 2nd swap
   -  -  -  -  -  -  -  -     *  +  +  +  +
   1, 8, 5, 2, 4, 0, 6, 3, 7, 9,12,13,10,11    // 3rd swap
   -  -  -  -  -  -  -  -  -  *  +  +  +  +
   1, 8, 5, 2, 4, 0, 6, 3, 7, 9,12,13,10,11    // return i = 10 
  /  /  /  /  /  /  /  /  /   \  \  \  \  \
  1, 8, 5, 2, 4, 0, 6, 3, 7,   9,12,13,10,11   

quickSort first part
   1, 8, 5, 2, 4, 0, 6, 3, 7                 
   *
   4, 8, 5, 2, 1, 0, 6, 3, 7                  // random swap
partition(a,9)
   -                    *  +
   3, 8, 5, 2, 1, 0, 6, 4, 7                  // 1st swap
   -  *                 +  +
   3, 4, 5, 2, 1, 0, 6, 8, 7                  // 1st swap
   -  -           *  +  +  +
   3, 0, 5, 2, 1, 4, 6, 8, 7                  // 3nd swap
   -  -  *        +  +  +  +
   3, 0, 4, 2, 1, 5, 6, 8, 7                  // 4th swap
   -  -  -     *  +  +  +  +
   3, 0, 1, 2, 4, 5, 6, 8, 7                  // 5th swap
   -  -  -  -  *  +  +  +  +
   3, 0, 1, 2, 4, 5, 6, 8, 7                  // return i = 4
  /  /  /  /    \  \  \  \  \
 3, 0, 1, 2,     4, 5, 6, 8, 7    

Quick sort is a randomized algorithm (choice of pivot for partitioning is random). It runs in expected time O(n log(n)). quickSort can be effectively combined with insertionSort. The point is that insertionSort, while disastrous on large arrays, is the fastest for small arrays. The strategy of quickinsSort is to adjust quickSort to take advantage of this.
template <typename T>
void quickinsSort(T *a, int n) { // sort a[0..n-1] into increasing order.
	if (n < THRESHOLD) { 
		insertionSort(a, n); 
		return; 
	}

	// place random entry in first position
	swap(a[0], a[rand()%n]);
	// partition based on that entry
	int i = partition(a, n);

	quickinsSort(a, i); // sort the smaller elts.
	quickinsSort(a+i, n-i); // sort the larger elts.
}
Like quickSort, quickinsSort runs in O(n log(n)) expected time, but is sped up by a constant factor by use of insertionSort on small segments of the array. clicker questions.
  1. Which divide and conquer sorting algorithm begins with two recursive calls and follows up by combining the two sorted bits appropriately?
    1. heapSort
    2. insertionSort
    3. mergeSort
    4. quickSort












  2. Which divide and conquer sorting algorithm ends with two recursive calls after first dividing up the array into two appropriate bits?
    1. heapSort
    2. insertionSort
    3. mergeSort
    4. quickSort












  3. Which two sorting algorithms can be combined effectively, even though one of them runs in the horrible time O(n^2)?
    1. heapSort and insertionSort
    2. insertionSort and mergeSort
    3. mergeSort and quickSort
    4. quickSort and insertionSort












  4. Which sorting algorithm is not "in place", that is, it requires allocation of additional array(s) for it's operation.
    1. heapSort
    2. insertionSort
    3. mergeSort
    4. quickSort