Please send questions to st10@humboldt.edu .

/**
 * Cs132ArrayHeap - using a complete heap implemented using an
 *                  array to implement a priority queue
 * 
 * @Sharon Tuttle
 * @last modified: 4-8-03
 */
public class Cs132ArrayHeap implements Cs132PriorityQueue
{
    /*----------------------------------------------------
     fields
    ------------------------------------------------------*/
    
    private Comparable data[];
    private int size;
    private static int BASE_AMT = 16; // should be a POWER OF 2!!!
    private int capacity;
    
    /*--------------------------------------------------------
     constructors
    ---------------------------------------------------------*/
    
    public Cs132ArrayHeap()
    {
        this.data = new Comparable[BASE_AMT];
        this.size = 0;
        this.capacity = BASE_AMT;
    }
    
    /*------------------------------------------
     overridden methods
    -------------------------------------------*/
    public String toString()
    {
        String heapAsString = "Cs132ArrayHeap[size = " + this.size 
                              + ", elements are:\n";
     
        // used to try and figure out when a newline is needed 
        int nextLevel = 2;
        
        for (int i = 0; i < size; i++)
        {
            heapAsString += this.data[i] + " ";
            
            // have we reached the end of a level?
            if ((i+2) == (nextLevel))
            {    
                heapAsString += "\n";
                nextLevel *= 2;
            }
        }
        
        heapAsString += "\n]\n";
        
        return heapAsString;
    }
    
    /*----------------------------------------------------------
     other methods 
    ------------------------------------------------------------
    
    /*------------------------------------------------------------
     getFirst()
     Purpose: return the minimum value in this heap
              (non-destructively). Returns null if heap
              is empty.
    --------------------------------------------------------------*/

    public Comparable getFirst()
    {
        if (this.size == 0)
        {  
            return null;
        }
        else
        {
            return this.data[0];
        }
    }

    /*-------------------------------------------------------------
     removeFirst()
     Purpose: remove (and return) the minimum value in this priority
              queue ("destructively" --- it DOES change the actual
              priority queue, removing the value). Returns null
              if priority queue is empty.
    ----------------------------------------------------------------*/

    public Comparable removeFirst()
    {
        // if heap is empty, just return null
        if (this.size == 0)
        {
            return null;
        }

        Comparable minItem = this.data[0];
        
        // grab rightmost leaf, put it into root --- then binary
        // tree is still complete, right?
        // (note that (size-1) is the index of the rightmost leaf!)
        this.data[0] = this.data[size-1];
        size = size - 1;  // now leaf is effectively "removed"!
        
        // "percolate" leaf value down through the tree to "reheap"
        // the tree back into a heap
        percolateDown(0);
        
        return minItem;
    }
    
    /*--------------------------------------------------------
     percolateDown()
     Purpose: given a tree that is a heap EXCEPT for the
              value at the index ind, "percolate" it down
              the tree until it IS again a heap
    -----------------------------------------------------------*/
    private void percolateDown(int ind)
    {
        int leftChildIndex = (2 * ind) + 1;
        int rightChildIndex = (2 * ind) + 2;
        Comparable rootValue = this.data[ind];
        
        // are we at a leaf? if so, then subtree is trivially
        // a heap, and we are done.
        //
        // (note that we can compare the child indices to size
        // of tree to see! 8-) )
        if ((leftChildIndex > (this.size-1)) &&
            (rightChildIndex > (this.size-1)))
        {
            return;
        }
        
        // if we reach here,
        // we are not at a leaf --- but we still might have a node
        // with one child but not two. (For a complete heap, can
        // you see that no node can have a right child without a
        // left, but one node can have a left without a right?)
        if (rightChildIndex > (this.size-1))
        {
            // we know there's a left child, or we wouldn't have
            // gotten this far! 8-)
            
            // note that this left child MUST be a leaf ---
            // play with complete heaps, and you'll see.
            
            if (rootValue.compareTo(this.data[leftChildIndex]) > 0)
            {
                // a swap with this child is necessary;
                this.data[ind] = this.data[leftChildIndex];
                this.data[leftChildIndex] = rootValue;
            }
            
            // but no recursive call needed --- we're done
            // at this point. Either needed swap was done,
            // or subtree was already a heap.
            return;
        }
        
        // if we get here --- there ARE two children for this subtree's
        // root node
        
        Comparable leftChildValue = this.data[leftChildIndex];
        Comparable rightChildValue = this.data[rightChildIndex];
        
        // if value at this index is less than both children,
        // then it is the true minimum for this subheap ---
        // heapdom has been restored!
        if ((rootValue.compareTo(leftChildValue) < 0) &&
            (rootValue.compareTo(rightChildValue) < 0))
        {
            // this is again a heap; we are done.
            return;
        }
        
        // if get here, one of children was less than this root ---
        // swap root with lesser child (true root of this subtree)
        int indexToSwap;
        if (leftChildValue.compareTo(rightChildValue) < 0)
        {
            indexToSwap = leftChildIndex;
        }
        else
        {
            indexToSwap = rightChildIndex;
        }
        
        this.data[ind] = this.data[indexToSwap];
        this.data[indexToSwap] = rootValue;
        
        // now make subtree where new value was placed 
        // a heap, if necessary
        percolateDown(indexToSwap);
    }

    /*--------------------------------------------------------
     percolateUp()
     Purpose: given a tree that is a heap EXCEPT for the
              value at this index ind, "percolate" it up the
              tree until it IS again a heap
    ------------------------------------------------------------*/
    private void percolateUp(int ind)
    {
        // BASE: are we at the root? Then we're done.
        if (ind == 0)
        {
            return; // OK to just return from a void function
        }
        
        // not at root? Then see if we're still done, anyway.
        // note that parent of ind is at data[(ind-1) / 2]
        int parentIndex = (ind-1) / 2;
        Comparable currNodeVal = this.data[ind];
        Comparable parentVal = this.data[parentIndex];
        
        // if currNodeVal is LESS than parentVal --- this isn't a heap yet!
        if (currNodeVal.compareTo(parentVal) < 0)
        {
            // ...swap parent and currNode
            data[parentIndex] = data[ind];
            data[ind] = parentVal;
            
            // handle from parent on up!
            percolateUp(parentIndex);
        }
        
        // else --- hey, currNode ISN'T less than parent ---
        // we're already a heap! That's BASE #2
        return;
    }
        
    /*----------------------------------------------------------------
     add()
     Purpose: Assuming that the value to be added is non-null, Comparable
              instance value is added to this heap.
    ------------------------------------------------------------------*/

    public void add(Comparable value)
    {
        // FIRST: if this will exceed capacity, recopy heap to
        // a new, larger data. Insert call to method that does that
        // here... 8-)
        if (this.size == this.capacity)
        {
            this.incrDataArray();
        }
        
        // now: new element can go in leftmost open spot on
        // lowest level. Then, tree will still be complete.
        // Hey, that's data[size]!
        this.data[size] = value;
        size = size + 1;
        
        // BUT --- now we've got to make this complete tree a heap.
        // HEY --- we know how! percolateUp()!
        percolateUp(size-1); // well, I *did* just increment size...
    }
    
    /*--------------------------------------------------------------
     incrDataArray()
     Purpose: copy heap into a larger array (2 times its current
              capacity --- if starts out with capacity a power of
              two, note that new capacity will be a power of 2, too!
    ----------------------------------------------------------------*/
    private void incrDataArray()
    {
        Comparable newData[] = new Comparable[this.capacity*2];
        this.capacity *= 2;
       
        for (int i=0; i < size; i++)
        {
            newData[i] = this.data[i];
        }
       
        this.data = newData;
    }
    
    /*-----------------------------------------------------------------
     isEmpty()
     Purpose: return true if there are no elements in the heap,
              false otherwise.
    ---------------------------------------------------------------------*/

    public boolean isEmpty()
    {
        return this.size == 0;
    }

    /*-------------------------------------------------------------------
     size()
     Purpose: returns number of elements within the priority queue
    --------------------------------------------------------------------*/
     
    public int size()
    {
        return this.size;
    }

    /*-------------------------------------------------------------------
     clear()
     Purpose: removes all elements from priority queue 
    ---------------------------------------------------------------------*/

    public void clear()
    {
        this.size = 0;
    }
}