/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.collections.queues; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.NoSuchElementException; import xxl.core.cursors.AbstractCursor; import xxl.core.cursors.Cursor; /** * This class provides the heap datastructure. The <tt>clear</tt>, * <tt>size</tt>, <tt>peek</tt> operations and the constructors that create * an empty heap run in constant time. The <tt>heapify</tt> operation * and the constructors that initialize the heap with an array * heap with an array of objects run in amortized constant time, that is, * adding n elements requires O(n) time. * * <p>The heap is stored in an internally used array that is never resized by * methods of this class. This class provides no methods to change the * maximum capacity of a heap. So remember that the internally used array * must be able to contain all the elements the heap should be able to * contain when creating a new heap. There are two way of ensuring the * capacity of a heap when constructing it * <ul> * <li>Constructing an empty heap with the constructor * <tt>Heap(int size)</tt> or * <tt>Heap(int size, Comparator comparator)</tt>. The internally used * array is set to <tt>new Object[size]</tt>.</li> * <li>Constructing a heap containing the elements of a specified array * with one of the other constructors. The internally used array is set to * the specified array. In this case the size of the specified array is * set to the maximal number of elements the heap is able to contain.</li> * </ul></p> * * <p>The order of the heap is determined by a comparator. More exactly, * there can be three different cases when two elements <tt>o1</tt> and * <tt>o2</tt> are inserted into the heap * <ul> * <dl> * <dt><li><tt>comparator.compare(o1, o2) < 0</tt> :</li></dt> * <dd>the heap returns <tt>o1</tt> prior to returning <tt>o2</tt>.</dd> * <dt><li><tt>comparator.compare(o1, o2) == 0</tt> :</li></dt> * <dd>when inserting equal elements (determined by the used comparator), * there is no guarantee which one will be returned first. * <dt><li><tt>comparator.compare(o1, o2) > 0</tt> :</li></dt> * <dd>the heap returns <tt>o2</tt> prior to returning <tt>o1</tt>.</dd> * </dl> * </ul></p> * * <p><b>Note:</b> This heap implementation has an anomaly: the tree has a * special root node.<br /> * This does not have any impact on working with this * heap class but the implementation is quite different from common heap * implementations. * <pre> * [0] <----- !!! * | * [1] * / \ * [2] [3] * / \ / \ * . . . . * . . . . * </pre></p> * * <p>Usage example (1). * <pre> * // create an array of objects to store in the heap * * Object[] array = new Object[] { * new Object[] { * new Integer(1), * new String("first") * }, * new Object[] { * new Integer(2), * new String("first") * }, * new Object[] { * new Integer(1), * new String("second") * }, * new Object[] { * new Integer(3), * new String("first") * }, * new Object[] { * new Integer(1), * new String("third") * } * }; * * // create a comparator that compares the objects by comparing their * // Integers * * Comparator<Object> comparator = new Comparator<Object>() { * public int compare(Object o1, Object o2) { * return ((Integer)((Object[])o1)[0]).intValue() * -((Integer)((Object[])o2)[0]).intValue(); * } * }; * * // create a heap that is initialized with the array (the heap has maximum capacity of * // 5 elements (array.length) and contains 5 elements) and that uses the given comparator * * Heap<Object> heap = new Heap<Object>(array, comparator); * * // open the heap * * heap.open(); * * // print the elements of the heap * * while (!heap.isEmpty()) { * Object[] o = (Object[])heap.dequeue(); * System.out.println("Integer = "+o[0]+" & String = "+o[1]); * } * System.out.println(); * * // close the open heap after use * * heap.close(); * </pre></p> * * <p>Usage example (2). * <pre> * // refresh the array (it was internally used into the heap and has changed) * * array = new Object[] { * new Object[] { * new Integer(1), * new String("first") * }, * new Object[] { * new Integer(2), * new String("first") * }, * new Object[] { * new Integer(1), * new String("second") * }, * new Object[] { * new Integer(3), * new String("first") * }, * new Object[] { * new Integer(1), * new String("third") * } * }; * * // create a heap that is initialized with the first three elements of the array (the heap * // has a maximum capacity of 5 elements (array.length) and contains 3 elements) and that * // uses the given comparator * * heap = new Heap<Object>(array, 3, comparator); * * // open the heap * * heap.open(); * * // print the elements of the heap * * while (!heap.isEmpty()) { * Object[] o = (Object[])heap.dequeue(); * System.out.println("Integer = "+o[0]+" & String = "+o[1]); * } * System.out.println(); * * // close the open heap after use * * heap.close(); * </pre></p> * * <p>Usage example (3). * <pre> * // refresh the array (it was internally used into the heap and has changed * * array = new Object[] { * new Object[] { * new Integer(1), * new String("first") * }, * new Object[] { * new Integer(2), * new String("first") * }, * new Object[] { * new Integer(1), * new String("second") * }, * new Object[] { * new Integer(3), * new String("first") * }, * new Object[] { * new Integer(1), * new String("third") * } * }; * * // create an empty heap with a maximum capacity of 5 elements and that uses the given * // comparator * * heap = new Heap<Object>(5, comparator); * * // open the heap * * heap.open(); * * // generate an iteration over the elements of the given array * * Cursor<Object> cursor = new ArrayCursor<Object>(array); * * // insert all elements of the given iterator * * for (; cursor.hasNext(); heap.enqueue(cursor.next())); * * // print the elements of the heap * while (!heap.isEmpty()) { * Object[] o = (Object[])heap.dequeue(); * System.out.println("Integer = "+o[0]+" & String = "+o[1]); * } * System.out.println(); * * // close the open heap and cursor after use * * heap.close(); * cursor.close(); * </pre></p> * * @param <E> the type of the elements of this queue. * @see xxl.core.collections.queues.Queue * @see xxl.core.collections.queues.AbstractQueue */ public class Heap<E> extends AbstractQueue<E> { /** * The array is internally used to store the elements of the heap. The * size of this array defines the maximum capacity of the heap. */ protected Object[] array; /** * Stores the current version of the heap. After each enqueue/dequeue * operation, the version counter is incremented. */ protected long version; /** * The comparator to determine the order of the heap. More exactly, * there can be three different cases when two elements <tt>o1</tt> * and <tt>o2</tt> are inserted into the heap * <ul> * <dl> * <dt><li><tt>comparator.compare(o1, o2) < 0</tt> :</dt> * <dd>the heap returns <tt>o1</tt> prior to returning <tt>o2</tt>.</dd> * <dt><li><tt>comparator.compare(o1, o2) == 0</tt> :</dt> * <dd>when inserting equal elements (determined by the used * comparator), there is no guarantee which one will be returned * first. * <dt><li><tt>comparator.compare(o1, o2) > 0</tt> :</dt> * <dd>the heap returns <tt>o2</tt> prior to returning <tt>o1</tt>.</dd> * </dl> * </ul> */ protected Comparator<? super E> comparator; /** * Constructs a heap containing the elements of the specified array * that returns them according to the order induced by the specified * comparator. The specified array has two different functions. First, * the heap depends on this array and is not able to contain more * elements than the array is able to. Second, it is used to initialize * the heap. The field <tt>array</tt> is set to the specified array, * the field <tt>last</tt> is set to the specified size - 1 and the * field <tt>comparator</tt> is set to the specified comparator. After * initializing the fields the heapify method is called. * * @param array the object array that is used to store the heap and * initialize the internally used array. * @param size the number of elements of the specified array which * should be used to initialize the heap. * @param comparator the comparator to determine the order of the * heap. * @throws IllegalArgumentException if the specified size argument is * negative, or if it is greater than the length of the * specified array. */ public Heap(E[] array, int size, Comparator<? super E> comparator) throws IllegalArgumentException { if (array.length < size || size < 0) throw new IllegalArgumentException(); this.array = array; this.size = size; this.comparator = comparator; version = 0; } /** * Constructs a heap containing the elements of the specified array * that returns them according to the order induced by the specified * comparator. This constructor is equivalent to the call of * <code>Heap(array, array.length, comparator)</code>. * * @param array the object array that is used to store the heap and * initialize the internally used array. * @param comparator the comparator to determine the order of the * heap. */ public Heap(E[] array, Comparator<? super E> comparator) { this(array, array.length, comparator); } /** * Constructs an empty heap with a capacity of <tt>size</tt> elements and * uses the specified comparator to order elements when inserted. This * constructor is equivalent to the call of * <code>Heap(new Object[size], 0, comparator)</code>. * * @param size the maximal number of elements the heap is able to * contain. * @param comparator the comparator to determine the order of the * heap. */ public Heap(int size, Comparator<? super E> comparator) { this.array = new Object[size]; this.size = 0; this.comparator = comparator; version = 0; } /** * Computes the heap for the first (<tt>size</tt>) elements of the * internally used array in O(n) time. */ protected void heapify() { version++; if (size > 1) { for (int i = size/2-1; i > 0; i--) { Object top = array[i/2]; bubbleUp((E)array[i], sinkIn(i)); array[i/2] = top; } enqueue(replace((E)array[--size])); } } /** * Inserts the specified object into the heap and overwrites the * element at the index <tt>i</tt> of the internally used array without * damaging the structure of the heap. The specified object is * inserted into the path from the root of the heap to the element * with the index <tt>i</tt> and the whole path beyond the inserted element * is <i>shifted</i> one level down. This method only works fine when the * following prerequisites are valid * <ul> * <li><tt>0 ≤ i ≤ last</tt></li> * <li><tt>comparator.compare(array[0], object) ≤ 0</tt></li> * </ul> * * @param object the object to insert into the heap. * @param i an index of the internally used array. The specified * object is inserted into the path from the root of the heap * to the element with the index i. */ protected void bubbleUp(E object, int i) { // prerequisite: 0 <= i <= last && array[0] <= object while (comparator.compare(object, (E)array[i/2]) < 0) { array[i] = array[i /= 2]; if (i == 0) break; } array[i] = object; } /** * Removes the element at the index i/2 of the internally used array * without damaging the structure of the heap. The whole path from the * element at the index i of the internally used array to the bottom * level of the heap is shifted one level up. This method only works * fine when the following prerequisite is valid * <ul> * <li><tt>1 ≤ i ≤ last</tt></li> * </ul> * * @param i an index of the internally used array. The object at the * index i/2 of the internally used array is removed. * @return the index of the last element in the path that is shifted * one level up. */ protected int sinkIn(int i) { // prerequisite: 1 <= i <= last array[i/2] = array[i]; while ((i *= 2) < size-1) array[(comparator.compare((E)array[i], (E)array[i+1]) < 0 ? i : i++)/2] = array[i]; return i/2; } /** * Opens the heap, i.e., signals the heap to reserve resources and * initializes itself. Before a queue has been opened calls to methods like * <tt>peek</tt> are not guaranteed to yield proper results. * Therefore <tt>open</tt> must be called before a queue's data * can be processed. Multiple calls to <tt>open</tt> do not have any effect, * i.e., if <tt>open</tt> was called the queue remains in the state * <i>opened</i> until its <tt>close</tt> method is called. * * <p>Note, that a call to the <tt>open</tt> method of a closed queue * usually does not open it again because of the fact that its state * generally cannot be restored when resources are released respectively * files are closed.</p> */ public void open() { if (isOpened) return; super.open(); heapify(); } /** * Removes all elements from this heap. The heap will be empty * after this call returns so that <tt>size() == 0</tt>. */ public void clear() { size = 0; version++; } /** * Inserts the specified element into this heap and restores the * structure of the heap if necessary. * * @param object element to be inserted into this heap. */ protected void enqueueObject(E object) { version++; if (size > 0) { if (comparator.compare(object, (E)array[0]) < 0) { E top = (E)array[0]; array[0] = object; object = top; } bubbleUp(object, size); } else array[0] = object; } /** * Returns the <i>next</i> element in the heap <i>without</i> removing it. * The <i>next</i> element of the heap is determined by the * comparator. * * @return the <i>next</i> element in the heap. */ protected E peekObject() { return (E)array[0]; } /** * Returns the <i>next</i> element in the heap and <i>removes</i> it. * The <i>next</i> element of the heap is determined by the comparator. * * @return the <i>next</i> element in the heap. */ protected E dequeueObject() { version++; E minimum = (E)array[0]; if (size > 1) bubbleUp((E)array[size-1], sinkIn(1)); return minimum; } /** * Replaces the next element in the heap with the specified object and * restore the structure of the heap if necessary. More exactly, the * specified object is inserted into the heap and the next element of * the heap is overwritten. Thereafter the structure of the heap is * restored and the overwritten element is returned. This method only * works fine when the following prerequisite is valid * <ul> * <li><tt>comparator.compare(array[0], object) ≤ 0</tt></li> * </ul> * * @param object element to be inserted into this heap. * @return the <i>next</i> element in the heap. * @throws NoSuchElementException queue has no more elements. */ public E replace(E object) throws NoSuchElementException { version++; // prerequisite: array[0] <= object if (isClosed) throw new IllegalStateException(); if (!isOpened) open(); if (size <= 0) throw new NoSuchElementException(); E minimum = (E)array[0]; if (size > 1 && comparator.compare(object, (E)array[1]) > 0) { int up = sinkIn(1); if (2*up == size-1) array[up] = array[up = size-1]; bubbleUp(object, up); } else array[0] = object; computedNext = false; return minimum; } /** * If an object is updated at position index, the heap * condition may be violated. This methode bubbles up the * updated element or lets it sink down, until the condition * is satisfied. * @param index The index inside the array where an update occured. */ protected void update(int index) { computedNext = false; bubbleUp((E)array[index], index); // sink without removal! int i=index; if (i==0) { if (comparator.compare((E)array[0], (E)array[1]) > 0 ) { Object tmp=array[0]; array[0] = array[1]; array[1] = tmp; i=1; } } if (i!=0) { while (2*i < size) { int smallerIndex=2*i; if (smallerIndex+1<size) if (comparator.compare((E)array[smallerIndex], (E)array[smallerIndex+1]) > 0 ) smallerIndex++; if (comparator.compare((E)array[smallerIndex], (E)array[i]) < 0 ) { Object tmp=array[i]; array[i] = array[smallerIndex]; array[smallerIndex] = tmp; i=smallerIndex; } else break; } } } /** * Returns a Cursor which supports reset, update and remove. * After an update or remove operation, the cursor is in an * invalid state. The only way to reuse the cursor then, is * to call reset. * @return A Cursor iterating over the elements of the heap. */ public Cursor<E> cursor() { return new AbstractCursor<E>() { private long myVersion=version; private int index=-1; protected boolean hasNextObject() { if (myVersion!=version) throw new ConcurrentModificationException(); return index+1<size; } protected E nextObject() { index++; return (E)array[index]; } public void remove() { if (size>1) { // array[size-1]>=array[0] array[index]=array[size-1]; size--; Heap.this.update(index); } else size=0; version++; } public boolean supportsRemove() { return true; } public void update(E object) { array[index]=object; Heap.this.update(index); version++; } public boolean supportsUpdate() { return true; } public void reset() { index=-1; myVersion = version; } public boolean supportsReset() { return true; } }; } /** * @return Returns a string representation of the heap. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("["); for (int i = 0; i < size; i++) { sb.append(((E)array[i]).toString()+ (i==size-1 ? "" : ", ")); } sb.append("]"); return sb.toString(); } }