Data Structures, Algorithms, & Applications in Java
Chapter 13, Exercise 29

Exercise 15 suggests an improvement to the removeMax method given in the text, and Exercise 16 suggests the use of two elements minElement and maxElement to reduce the total number of comparisons being performed. Combining these ideas results in the class MaxHeapWithMinAndMaxElements given below.
public class MaxHeapWithMinAndMaxElements implements MaxPriorityQueue
{
   // data members
   Comparable [] heap;      // array for complete binary tree
   int size;                // number of elements in heap
   Comparable minElement;   // all elements must be >= minElement
   Comparable maxElement;   // all elements must be <= maxElement

   // constructors
   /** create a heap with the given initial capacity */
   public MaxHeapWithMinAndMaxElements(int initialCapacity,
                 Comparable theMinElement, Comparable theMaxElement)
   {
      if (initialCapacity < 1)
         throw new IllegalArgumentException
                   ("initialCapacity must be >= 1");
      heap = new Comparable [2 * (initialCapacity + 1)];
      size = 0;
      maxElement = theMaxElement;
      minElement = theMinElement;

      // put maxElement in heap[0]
      heap[0] = maxElement;

      // put minElement in all other positions
      for (int i = 1; i < heap.length; i++)
         heap[i] = minElement;
   }
   
   /** create a heap with initial capacity 10 */
   public MaxHeapWithMinAndMaxElements(Comparable theMinElement,
                                       Comparable theMaxElement)
      {this(10, theMinElement, theMaxElement);}

   // methods
   /** @return true iff the heap is empty */
   public boolean isEmpty()
      {return size == 0;}

   /** @return number of elements in the heap */
   public int size()
      {return size;}

   /** @return maximum element
     * @return null if the heap is empty */
   public Comparable getMax()
   {
      if (size == 0)
         return null;
      else
         return heap[1];
   }

   /** put theElement into the heap */
   public void put(Comparable theElement)
   {
      // validate new element
      if (theElement.compareTo(minElement) < 0 ||
          theElement.compareTo(maxElement) > 0)
         throw new IllegalArgumentException
               ("element must be within specified range");

      // increase array size if necessary
      if (size == heap.length / 2 - 1)
      {// double array size and fill new positions with minElement
         int oldLength = heap.length;
         heap = (Comparable []) ChangeArrayLength.changeLength1D
                                    (heap, 2 * heap.length);
         for (int i = oldLength; i < heap.length; i++)
            heap[i] = minElement;
      }

   
      // find place for theElement
      // i starts at new leaf and moves up tree
      int i = ++size;
      // no need to check if root reached
      // because heap[0] = MaxElement
      while (heap[i / 2].compareTo(theElement) < 0)
      {
         // cannot put theElement in heap[i]
         heap[i] = heap[i / 2]; // move element down
         i /= 2;                // move to parent
      }
   
      heap[i] = theElement;
   }
   
   /** remove max element and return it */
   public Comparable removeMax()
   {
      // if heap is empty return null
      if (size == 0)
         return null;                   // heap empty
   
      Comparable x = heap[1];           // max element
   
      // restucture heap
      Comparable y = heap[size];        // last element
   
      // replace with minElement
      heap[size--] = minElement;
   
      // first propagate vacancy to a leaf
      int i = 1,                     // current position of vacancy
          ci = 2;                    // child of i
      while (ci <= size)
      {
         // heap[ci] should be larger child of i
         // no need to check if ci < size
         // because heap[size] has minElement
         if (heap[ci].compareTo(heap[ci + 1]) < 0)
            ci++;
   
         // move larger child to heap[i]
         heap[i] = heap[ci];         // move child up
         i = ci;                     // move down a level
         ci *= 2;
      }
   
      i = ci / 2;
      // vacancy is now at heap[i], start from here and insert y
      // no need to check if root reached
      // because heap[0] = maxElement
      while (y.compareTo(heap[i / 2]) > 0)
      {// cannot put y in heap[i]
         heap[i] = heap[i / 2];      // move element down
         i /= 2;                     // move to parent
      }
   
      heap[i] = y;
   
      return x;
   }
   
   /** initialize max heap to element array theHeap */
   public void initialize(Comparable [] theHeap, int theSize)
   {
      heap = theHeap;
      size = theSize;
   
      if (2 * (size + 1) < heap.length)
         // double array size
         heap = (Comparable []) ChangeArrayLength.changeLength1D
                                    (heap, 2 * heap.length);

      // fill unused positions with minElement
      for (int i = size + 1; i < heap.length; i++)
         heap[i] = minElement;

      // put maxElement into heap[0]
      heap[0] = maxElement;

      // make into a max heap by running old method as
      // we do not expect to propagate all the way down
      for (int i = size / 2; i >= 1; i--)
      {
         Comparable y = heap[i]; // root of subtree
   
         // find place to put y
         int c = 2 * i; // parent of c is target
                        // location for y
         while (c <= size)
         {
            // heap[c] should be larger sibling
            // no need to check if c < size as
            // all unused spots have minElement
            if (heap[c].compareTo(heap[c + 1]) < 0)
               c++;
   
            // can we put y in heap[c/2]?
            if (y.compareTo(heap[c]) >= 0)
               break;  // yes
   
            // no
            heap[c / 2] = heap[c]; // move child up
            c *= 2;                // move down a level
         }
         heap[c / 2] = y;
      }
   }
}



We must also change the code for the heap sort method so that it conforms to the requirements of the changed constructor method of MaxHeapWithMinAndMaxElements. This requires us to define minElement and maxElement. The new code is given below.
public class NewHeapSort
{
   /** sort the elements a[1 : a.length - 1] using
     * the heap sort method */
   public static void heapSort(Comparable [] a)
   {
      if (a.length <= 2)
         // array has fewer than two elements
         return;

      // more than 1 element, first find min and max elements
      Comparable min = a[1];
      Comparable max = a[1];
      for (int i = 2; i < a.length; i++)
         if (min.compareTo(a[i]) > 0)
            min = a[i];
         else
            if (max.compareTo(a[i]) < 0)
               max = a[i];
   
      // create a max heap of the elements
      MaxHeapWithMinAndMaxElements h =
             new MaxHeapWithMinAndMaxElements(1, min, max);
      h.initialize(a, a.length - 1);
   
      // extract one by one from the max heap
      for (int i = a.length - 1; i >= 1; i--)
         a[i] = h.removeMax();
        
   }
}



The experimental comparison with the original implementation (i.e., HeapSort.heapSort) has not been done.