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