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 "level k" is the maximum possible) AND any "missing node" on level h 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 missing):

                             +
                            / \
                           /   \
                          /     \
                         /       \
                        /         \
                       /           \
                      /             \
                     /               \
                    /                 \
                   /           +-------+  
                  +------------+   

Definition: A min-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 less than or equal to the key value in v. (In a max-heap, replace "less" in the sentence above with "greater".)

Definition: A priority queue is a collection of items, each of which has associated with it a "priority". For simplicity, we assume that priorities are expressed via integers and that the smaller the integer, the higher the priority it represents. Three operations are defined on priority queues:

These operations are analogous to the enqueue(), dequeue(), and frontOf() operations, respectively, on a (plain old) queue.

A heap is an effective way of representing a priority queue.

public class PriorityQueue {


   /* pre:  initSize > 0  &&  comp defines a total ordering on the 
    *       universe of objects from which will be drawn the elements
    *       that are inserted into the PQ.
    * post: initializes 'this' with room enough for initSize elements
   */
   public PriorityQueue(Comparator comp, int initSize)  {
      b = new Object[initSize];
      nodeCntr = 0;
      c = comp;
   }
 
   /* pre:  comp defines a total ordering
      post: constructs an empty PQ 
   */
   public PriorityQueue(Comparator comp)
      { this(comp, defaultInitSize); }

   public void insert(Object x) {

      if (nodeCntr == b.length) {   // if b is full, double its length
         Object[] temp = b;
         b = new Object[2*nodeCntr];
         for (int i=0;  i != b.length;  i = i+1)
            { b[i] = temp[i]; }
      } 

      b[nodeCntr] = x;
      siftUp(nodeCntr);
      nodeCntr = nodeCntr + 1;
   }    

   public int getMin()  { return b[0]; }

   public void deleteMin() {

      b[0] = b[nodeCntr - 1];
      nodeCntr = nodeCntr - 1;
      siftDown(0);
   }    

   /*  <<<<     P R I V A T E    >>>>  */


   /* <<<<  i n s t a n c e   v a r i a b l e s  >>>> */

   private Comparator c;    // for comparing items in heap
   private Object[] b;      // array to hold values in heap
   private int nodeCntr     // b[0..nodeCntr-1] contain values in heap

   private static final int defaultInitSize = 8;

   private boolean lessThan(Object a, Object b)
      { return c.compare(a, b) < 0; }


   /*  <<<< 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; }


   /*  <<<< 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); }

   private siftUp(int k) {

      if (k = rootLoc()) { }
      else if (lessThan(b[k], b[parentLoc(k)])) {
         swap(b, k, parentLoc(k) );
         siftup( parentLoc(k) );
      }
   }

   private siftUpIterative(int k) {

      while ( k != rootLoc()  &&  lessThan(b[k], b[parentLoc(k)]) )  {
         swap(b, k, parentLoc(k) );
         k = parentLoc(k);
      }
   }


   private siftDown(int k) {

      if ( isLeaf(k) ) { }
      else {
         int leftLoc  = leftChildLoc(k);
         int rightLoc = rightChildLoc(k);
         int lesserChildLoc;
         if ( hasRightChild(k) )  {
            if ( lessThan( b[leftLoc], b[rightLoc] ) )
               { lesserChildLoc = leftLoc; }
            else
               { lesserChildLoc = rightLoc; }
         else
            { lesserChildLoc = leftLoc; }
         }
 
         if ( lessThan( b[lesserChildLoc], b[k] ) ) {
            swap(b, k, lesserChildLoc );
            siftDown( lesserChildLoc );
         }
      }
   }