package com.limegroup.gnutella.util; import java.util.Iterator; import java.util.NoSuchElementException; /** * A class for maintaining the objects in a binary heap form, i.e., a classic * fixed-size priority queue. Its a MAX heap, i.e., the root of the heap is the * element with max value. The objects to be inserted into the heap must * implement java.lang.Comparable interface, as that is what is used for * comparison purposes so as to order the objects in the heap form. While in * the heap, these objects must not be mutated in a way that affects compareTo. * <b>This class is not synchronized; that is up to the user.</b><p> * * BinaryHeap now contains a constructor to allow dynamic resizing as well.<p> * * @see FixedsizePriorityQueueTest */ public class BinaryHeap { /** * number of elements currently present in the heap */ private int currentSize; /** * The array to keep the elements of the heap */ private Comparable[] array; /** * The maximum number of elements that can be put in the heap. Memory * allocated is maxSize+1 elements, as zeroth element is not used in the * array, for convenience in heap operations. */ private int maxSize; /** * True if we should dynamically resize this as needed. */ private boolean resizable=false; /** * Constructs a new fixed-size BinaryHeap. * * @param size the maximum size of the heap */ public BinaryHeap(int maxSize) { this(maxSize, false); } /** * Constructs a new BinaryHeap to initially hold the given number of * elements. Iff resize is true, the heap will grow dynamically to allow * more elements as needed. * * @param size the initial size of the heap * @param resizable true iff this should grow the heap to allow more * elements */ public BinaryHeap(int maxSize, boolean resizable) { this.resizable=resizable; currentSize = 0; this.maxSize = maxSize; array = new Comparable[maxSize + 1]; } /** * @modifes this * @effects removes all elements from this */ public void clear() { currentSize = 0; } /** * Initializes the array with the passed array Also takes the length of the * array and sets it as the currentSize as well as maxSize for the heap, and * makes heap out of that. The first element in the array (at location 0) * shouldn't contain any data, as it is discraded. The array is assumed to * be having values starting from location 1. * * @see BinaryHeap#currentSize * @see BinaryHeap#maxSize */ public BinaryHeap(Comparable[] array) { this.array = array; this.currentSize = array.length -1; this.maxSize = currentSize; buildHeap(); } /** * If this is resizable and if the heap is full, allocates more memory. * Returns true if the heap was actually resized. */ private boolean resize() { if (! isFull()) return false; if (! resizable) return false; //Note that currentSize is not changed. Also, note that first element //of array is not used. this.maxSize = currentSize*2; Comparable[] newArray=new Comparable[1+maxSize]; System.arraycopy(array, 1, newArray, 1, currentSize); this.array = newArray; return true; } /** * Used to maintain the heap property When heapify is called, it is assumed * that the binary trees rooted at array[2i] (left child), and array[2i+1] * (right child) are heaps, but array[i] may be smaller than its children, * thus violating the heap property. The function of heapify is to let the * value at array[i] float down in the heap so that the subtree rooted at * index i becomes a heap. */ private void heapify(int i) { int l = 2 * i; int r = 2 * i + 1; int largest; //compare array[i] with the left child to see if it is bigger than //array[i] set the largest as the larger of the two if((l <= currentSize) && (array[l].compareTo(array[i]) > 0)) { largest = l; } else { largest = i; } //compare array[largest] with the right child to see if it is bigger //set the largest as the larger of the two if((r <= currentSize) && (array[r].compareTo(array[largest]) > 0)) { largest = r; } //check if array[i] is indeed smaller than one of the children if(largest != i) { //swap array[i] with the larger of the two children swap(i, largest); //now heapify again the rest of the heap heapify(largest); } }//end of fn heapify /** * Makes the heap out of the elements in the array (may be in jumbled form * initially). After this method finishes, the array elements are in th * eform of heap structure This operation is O(n), where n is the number of * elements present in the array * * @see BinaryHeap#currentSize */ private void buildHeap() { //Nodes array[currentSize/2 +1 .....currentSize] are the leaves //So, we need not heapify them //So, we heapify rest of the elements //This operation is O(n) for(int i = currentSize/2; i >=1 ; i--) { heapify(i); } } /** * The function to swap two elements in the array * param i array[i] gives the first element * param j array[j] gives the second element */ private void swap(int i, int j) { Comparable temp = array[i]; array[i] = array[j]; array[j] = temp; } /** * @modifies this * @effects inserts x into this. If this is full, one of the "smaller" * elements of this (i.e., not the largest if this has more than one * element, though not necessarily the smallest) is removed and returned. * Otherwise, returns null; */ public Comparable insert(Comparable x) { resize(); Comparable ret=null; //Normal case if (currentSize<maxSize) { currentSize++; } //Overflow else { ret=array[currentSize]; } //Assume that the object is placed in the currentSize+1 location Compare //x with its parent. If x is larger than the parent, swap the parent and //x. Now again repeat the steps now that the x is in the new swapped //position int i; for(i = currentSize; (i > 1) && (x.compareTo(array[i/2]) > 0); i = i/2) { array[i] = array[i/2]; } array[i] = x; return ret; }//end of insert /** * Returns the largest element in this, without modifying this. If this is * empty, throws NoSuchElementException instead. */ public Comparable getMax() throws NoSuchElementException { if(currentSize < 1) throw new NoSuchElementException(); //first element (root) is the max return it return array[1]; } /** * @modifies this * @effects removes and returns the largest element in this. * If this is empty, throws NoSuchElementException instead. */ public Comparable extractMax() throws NoSuchElementException { //check if there is atleast one element in the heap if(currentSize < 1) throw new NoSuchElementException(); //first element (root) is the max //save it, swap first and last element, decrease the size of heap by one //and heapify it from the root Comparable max = array[1]; array[1] = array[currentSize]; array[currentSize] = null; // allow GC to clean the object later on. currentSize--; heapify(1); //return the max element return max; } /** * @requires this not modified while iterator in use * @effects returns an iterator that yields the max element first, then the * rest of the elements in any order. */ public Iterator iterator() { //TODO1: test me! return new BinaryHeapIterator(); } class BinaryHeapIterator extends UnmodifiableIterator { int next=1; public boolean hasNext() { return next<=currentSize; } public Object next() throws NoSuchElementException { if (! hasNext()) throw new NoSuchElementException(); return array[next++]; } } /** Returns the number of elements in this. */ public int size() { return currentSize; } /** Returns the maximum number of elements in this without growing the * heap. */ public int capacity() { return maxSize; } /** Returns true if this cannot store any more elements without growing the * heap, i.e., size()==capacity(). */ public boolean isFull() { return currentSize==maxSize; } /** Returns true if this is empty, i.e., size()==0 */ public boolean isEmpty() { return currentSize==0; } public String toString() { StringBuffer ret=new StringBuffer("["); for (Iterator iter=iterator(); iter.hasNext(); ) { ret.append(iter.next().toString()); ret.append(", "); } ret.append("]"); return ret.toString(); } }//end of class BinaryHeap