CMPS 144
Heaps

Definition: A (rooted) binary tree T is either empty or else it consists of a node u, called the root, and two (embedded) binary trees, called the left and right subtrees of T. If the left (respectively, right) subtree of T is nonempty (meaning it has a root node v), then v is referred to as the left (resp., right) child of u.

                              +--+
                              |  |   <--- root
                              +--+
                             /    \
                            /      \
                           /        \
                          +          +
                         / \        / \
  left subtree --->     /   \      /   \    <--- right subtree
                       /     \    /     \
                      +-------+  +-------+ 

Definition: The height of an empty (rooted binary) tree is -1. The height of a non-empty (rooted binary) tree is one more than the larger of the heights of its left and right subtrees.

            +
           / \
          /   \
         /     \
        /       \
       /         \
      /           \
     /             \
    /               \
   /                 \
  /           +-------+  
 /            |
+-------------+
Definition: A (rooted) binary tree T of height h>0 is said to be complete if, for all k satisfying 0≤k<h, the number of nodes at distance k from the root of T is 2k (which is to say that the number of nodes on all such levels is the maximum possible) AND any "absent" nodes on level h (the "bottommost" level) must be to the right of all (existing) nodes on that level. Hence, the outline of a complete binary tree looks like a Christmas tree, possibly with a chunk missing from the bottom right corner (where nodes are absent), as suggested by the figure to the right.

Definition: A max-heap is a complete binary tree satisfying the property that, for any nodes u and v, if u is the parent of v, then the "key value" in u is greater than or equal to the key value in v. (In a min-heap, replace "greater" in the sentence above with "less".)

Max-heaps support only three significant operations:

These operations are analogous to the frontOf(), dequeue(), and enqueue() operations, respectively, on a queue. Indeed, a max-heap is a typical way to implement a priority queue, which is like a standard queue except that the next item to be removed is not the one that was in the queue the longest, but rather the item having highest priority (as measured by the associated key value).

public class MaxHeap<T> {

   // class constant
   // --------------

   private final int DEFAULT_INIT_CAPACITY = 16;
   private final int MIN_CAPACITY = 8;

   // instance variables
   // ------------------

   private T[] contents;   // array holding elements 
   private Comparator<T> c; 
   private int nodeCntr;   // # elements in this priority queue


   // constructor
   // -----------

   /* pre:  initCapacity > 0  and  comp defines a total ordering on 
   **       values of type T.
   ** post: this max-heapis empty (but with room enough for 
   **       initCapacity elements
   */
   public MaxHeap(Comparator<T> comp, int initCapacity)  {
      this.contents = (T[])(new Object[initCapacity]);
      this.nodeCntr = 0;
      this.c = comp;
   }
 
   /* pre:  comp defines a total ordering on values of type T
   ** post: this max-heap is empty (but with room enough for 
   **       a number of elements equal to a default capacity).
   */
   public MaxHeap(Comparator<T> comp)
      { this(comp, DEFAULT_INIT_CAPACITY); }


   // observers
   // ---------

   /* Returns the number of elements in this max-heap.
   */
   public int sizeOf() { return nodeCntr; }

   /* Reports whether this max-heap is empty.
   */
   public boolean isEmtpy() { return sizeOf() == 0; }

   /* Returns an element in this max-heap having a key
   ** value at least as great as every other element. 
   */
   public int getMax()  { return contents[0]; }


   // mutators
   // --------

   /* Inserts the given element into this max-heap.
   */
   public void insert(T x) {

      if (nodeCntr == contents.length) {   // if b is full, double its length
         resizeArray(2 * nodeCntr);
      }
      contents[nodeCntr] = x;
      siftUp(nodeCntr);
      nodeCntr = nodeCntr + 1;
   }    

   /* Removes the element of this max-heap that would be returned
   ** by calling getMax().  For convenience, also returns that element.
   */
   public T deleteMax() {
      T result = getMax();
      contents[0] = contents[nodeCntr - 1];
      nodeCntr = nodeCntr - 1;
      siftDown(0);
      if (nodeCntr < contents.length / 4  &&  nodeCntr > MIN_CAPACITY)
         resizeArray(nodeCntr / 2);
      }
      return result;
   }    


   // private methods
   // ---------------

   /* For the purpose of resizing contents[] when either it becomes
   ** full or too close to empty.
   */
   private void resizeArray(int newSize) {
      T[] newArray = (T[])(new Object(newSize));
      for (int i=0; i != nodeCntr; i++) {
         newArray[i] = contents[i]);
      }
      contents = newArray;
   }


   /* Returns true iff the first argument is greater than the
   ** second, according to the resident comparator.
   */
   private boolean greaterThan(T a, T b)
      { return c.compare(a, b) > 0; }


   //  tree navigation operations 
   //  --------------------------

   private int rootLoc() { return 0; }
   private int parentLoc(int v) { return (v-1) / 2; }
   private int leftChildLoc(int v)  { return 2*v + 1; }
   private int rightChildLoc(int v) { return 2*v + 2; }


   // tree observer operations 
   // ------------------------

   private boolean hasLeftChild(int v) 
      { return leftChildLoc(v) < nodeCntr; }

   private boolean hasRightChild(int v)
      { return rightChildLoc(v) < nodeCntr; }

   private boolean isLeaf(int v)  { return !hasLeftChild(v); }

   /* Swaps the item stored in node #k upwards towards the root
   ** until either it reaches the root or is not greater than the 
   ** item in its parent node, whichever comes first.
   ** (This version is intended to be recursive.)
   */
   private siftUp(int k) {

      // code omitted

   }

   /* Swaps the item stored in node #k upwards towards the root
   ** until either it reaches the root or is not greater than the 
   ** item in its parent node, whichever comes first.
   ** (This version is intended to use a loop.)
   */
   private siftUpIterative(int k) {

      // code omitted

   }


   /* Swaps the item stored in node #k downwards towards its
   ** descendant leaves until either it reaches a leaf or is 
   ** at least as large as the item(s) in its child(ren),
   ** whichever comes first.
   ** (This version is intended to be recursive.)
   */
   private siftDown(int k) {

      // code omitted

   }

   /* Swaps the item stored in node #k downwards towards its
   ** descendant leaves until either it reaches a leaf or is 
   ** at least as large as the item(s) in its child(ren),
   ** whichever comes first.
   ** (This version is intended to use a loop.)
   */
   private siftDownIterative(int k) {

      // code omitted