Java tutorial
//package com.aliasi.util; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; /** * A <code>MinMaxHeap</code> provides a heap-like data structure that * provides fast access to both the minimum and maximum elements of * the heap. Each min-max heap is of a fixed maximum size. A min-max * heap holds elements implementing the {@link Scored} interface, with * scores being used for sorting. * * <p>A min-max heap data structure is useful to implement priority * queues with fixed numbers of elements, which requires access to * both the best and worst elements of the queue. * * <p>This implementation is based on the paper: * <ul> * <li>Atkinson, M.D., J.-R. Sack, N. Santoro and T. Strothotte. * 1986. * <a href="http://www.cs.otago.ac.nz/staffpriv/mike/Papers/MinMaxHeaps/MinMaxHeaps.pdf">Min-max heaps and generalized priority queues</a>. * <i>Communications of the ACM</i> <b>29</b>(10):996-1000. * </li> * </ul> * * @author Bob Carpenter * @version 3.8 * @since LingPipe2.4.0 * @param <E> the type of objects stored in the heap */ public class MinMaxHeap<E extends Scored> extends AbstractCollection<E> { // index starts at 1 to follow article // min at root and at even levels; max at root dtrs private final E[] mHeap; private final int mHeapLength; // true for min levels private final boolean[] mLevelTypes; private int mNextFreeIndex = 1; /** * Construct a min-max heap holding up to the specified * number of elements. Min-max heaps do not grow * dynamically and attempts to push an element onto a full * heap will result in exceptions. * * @param maxSize Maximum number of elements in the heap. */ public MinMaxHeap(int maxSize) { if (maxSize < 1) { String msg = "Heaps must be at least one element." + " Found maxSize=" + maxSize; throw new IllegalArgumentException(msg); } // required for array @SuppressWarnings("unchecked") E[] tempHeap = (E[]) new Scored[maxSize + 1]; mHeap = tempHeap; mHeapLength = mHeap.length; mLevelTypes = new boolean[maxSize + 1]; fillLevelTypes(mLevelTypes); } /** * Returns the current size of this heap. * * @return The size of the heap. */ @Override public int size() { return mNextFreeIndex - 1; } /** * Returns an iterator over this heap. The elements are returned * in decreasing order of score. * * @return Iterator over this heap in decreasing order of score. */ @Override public Iterator<E> iterator() { ArrayList<E> list = new ArrayList<E>(); for (int i = 0; i < mNextFreeIndex; ++i) list.add(mHeap[i]); Collections.<E>sort(list, ScoredObject.reverseComparator()); return list.iterator(); } /** * Add the element to the heap. * * @param s Element to add to heap. * @return <code>true</code> if the element is added. */ @Override public boolean add(E s) { // space still left if (mNextFreeIndex < mHeapLength) { mHeap[mNextFreeIndex++] = s; bubbleUp(mNextFreeIndex - 1); return true; } else if (s.score() <= peekMin().score()) { return false; } else { popMin(); mHeap[mNextFreeIndex++] = s; bubbleUp(mNextFreeIndex - 1); return true; } } /** * Returns the maximum element in the heap, or <code>null</code> * if it is empty. * * @return The largest element in the heap. */ public E peekMax() { return (mNextFreeIndex == 1 ? null : (mNextFreeIndex == 2 ? mHeap[1] : (mNextFreeIndex == 3 ? mHeap[2] : (mHeap[2].score() > mHeap[3].score() ? mHeap[2] : mHeap[3])))); } /** * Returns the minimum element in the heap, or <code>null</code> * if it is empty. * * @return The smallest element in the heap. */ public E peekMin() { return (mNextFreeIndex == 1) ? null : mHeap[1]; } /** * Returns the maximum element in the heap after removing it, or * returns <code>null</code> if the heap is empty. * * @return The largest element in the heap. */ public E popMax() { if (mNextFreeIndex == 1) return null; // only one element; return it if (mNextFreeIndex == 2) { --mNextFreeIndex; return mHeap[1]; } // two elements, so max is only on second level if (mNextFreeIndex == 3) { --mNextFreeIndex; return mHeap[2]; } // at least three elements, so check level 1 dtrs if (mHeap[2].score() > mHeap[3].score()) { E max = mHeap[2]; mHeap[2] = mHeap[--mNextFreeIndex]; trickleDownMax(2); return max; } else { E max = mHeap[3]; mHeap[3] = mHeap[--mNextFreeIndex]; trickleDownMax(3); return max; } } /** * Returns the minimum element in the heap after removing it, or * returns <code>null</code> if the heap is empty. * * @return The smallest element in the heap. */ public E popMin() { if (mNextFreeIndex == 1) return null; if (mNextFreeIndex == 2) { mNextFreeIndex = 1; return mHeap[1]; } E min = mHeap[1]; mHeap[1] = mHeap[--mNextFreeIndex]; trickleDownMin(1); return min; } /** * Returns a string-based representation of this heap. * * @return String representation of this heap. */ @Override public String toString() { if (mNextFreeIndex == 1) return "EMPTY HEAP"; StringBuilder sb = new StringBuilder(); for (int i = 1; i < mNextFreeIndex; ++i) { if (i > 1) sb.append("\n"); sb.append(i + "=" + mHeap[i]); } return sb.toString(); } void bubbleUp(int nodeIndex) { if (!hasParent(nodeIndex)) return; int parentIndex = parentIndex(nodeIndex); if (onMinLevel(nodeIndex)) { if (mHeap[nodeIndex].score() > mHeap[parentIndex].score()) { swap(nodeIndex, parentIndex); bubbleUpMax(parentIndex); } else { bubbleUpMin(nodeIndex); } } else { // on max level if (mHeap[nodeIndex].score() < mHeap[parentIndex].score()) { swap(nodeIndex, parentIndex); bubbleUpMin(parentIndex); } else { bubbleUpMax(nodeIndex); } } } void bubbleUpMin(int nodeIndex) { while (true) { if (!hasParent(nodeIndex)) return; int parentIndex = parentIndex(nodeIndex); if (!hasParent(parentIndex)) return; int grandparentIndex = parentIndex(parentIndex); if (mHeap[nodeIndex].score() >= mHeap[grandparentIndex].score()) return; swap(nodeIndex, grandparentIndex); nodeIndex = grandparentIndex; } } void bubbleUpMax(int nodeIndex) { while (true) { if (!hasParent(nodeIndex)) return; int parentIndex = parentIndex(nodeIndex); if (!hasParent(parentIndex)) return; int grandparentIndex = parentIndex(parentIndex); if (mHeap[nodeIndex].score() <= mHeap[grandparentIndex].score()) return; swap(nodeIndex, grandparentIndex); nodeIndex = grandparentIndex; } } boolean onMinLevel(int nodeIndex) { return mLevelTypes[nodeIndex]; } void trickleDown(int nodeIndex) { if (noChildren(nodeIndex)) return; if (onMinLevel(nodeIndex)) trickleDownMin(nodeIndex); else trickleDownMax(nodeIndex); } void trickleDownMin(int nodeIndex) { while (leftDaughterIndex(nodeIndex) < mNextFreeIndex) { // has dtrs int minDescIndex = minDtrOrGrandDtrIndex(nodeIndex); if (isDaughter(nodeIndex, minDescIndex)) { if (mHeap[minDescIndex].score() < mHeap[nodeIndex].score()) swap(minDescIndex, nodeIndex); return; } else { // is grand child if (mHeap[minDescIndex].score() >= mHeap[nodeIndex].score()) return; swap(minDescIndex, nodeIndex); int parentIndex = parentIndex(minDescIndex); if (mHeap[minDescIndex].score() > mHeap[parentIndex].score()) swap(minDescIndex, parentIndex); nodeIndex = minDescIndex; // recursive call in paper } } } void trickleDownMax(int nodeIndex) { while (leftDaughterIndex(nodeIndex) < mNextFreeIndex) { int maxDescIndex = maxDtrOrGrandDtrIndex(nodeIndex); if (isDaughter(nodeIndex, maxDescIndex)) { if (mHeap[maxDescIndex].score() > mHeap[nodeIndex].score()) swap(maxDescIndex, nodeIndex); return; } else { // is grand child if (mHeap[maxDescIndex].score() <= mHeap[nodeIndex].score()) return; swap(maxDescIndex, nodeIndex); int parentIndex = parentIndex(maxDescIndex); if (mHeap[maxDescIndex].score() < mHeap[parentIndex].score()) swap(maxDescIndex, parentIndex); nodeIndex = maxDescIndex; // recursive call in paper } } } // requires nodeIndex to have a dtr int minDtrOrGrandDtrIndex(int nodeIndex) { // start with left dtr; must have a dtr coming in int leftDtrIndex = leftDaughterIndex(nodeIndex); int minIndex = leftDtrIndex; double minScore = mHeap[leftDtrIndex].score(); int rightDtrIndex = rightDaughterIndex(nodeIndex); if (rightDtrIndex >= mNextFreeIndex) return minIndex; double rightDtrScore = mHeap[rightDtrIndex].score(); if (rightDtrScore < minScore) { minIndex = rightDtrIndex; minScore = rightDtrScore; } int grandDtr1Index = leftDaughterIndex(leftDtrIndex); if (grandDtr1Index >= mNextFreeIndex) return minIndex; double grandDtr1Score = mHeap[grandDtr1Index].score(); if (grandDtr1Score < minScore) { minIndex = grandDtr1Index; minScore = grandDtr1Score; } int grandDtr2Index = rightDaughterIndex(leftDtrIndex); if (grandDtr2Index >= mNextFreeIndex) return minIndex; double grandDtr2Score = mHeap[grandDtr2Index].score(); if (grandDtr2Score < minScore) { minIndex = grandDtr2Index; minScore = grandDtr2Score; } int grandDtr3Index = leftDaughterIndex(rightDtrIndex); if (grandDtr3Index >= mNextFreeIndex) return minIndex; double grandDtr3Score = mHeap[grandDtr3Index].score(); if (grandDtr3Score < minScore) { minIndex = grandDtr3Index; minScore = grandDtr3Score; } int grandDtr4Index = rightDaughterIndex(rightDtrIndex); if (grandDtr4Index >= mNextFreeIndex) return minIndex; double grandDtr4Score = mHeap[grandDtr4Index].score(); return grandDtr4Score < minScore ? grandDtr4Index : minIndex; } // requires nodeIndex to have a dtr int maxDtrOrGrandDtrIndex(int nodeIndex) { // start with left dtr; must have a dtr coming in int leftDtrIndex = leftDaughterIndex(nodeIndex); int maxIndex = leftDtrIndex; double maxScore = mHeap[leftDtrIndex].score(); int rightDtrIndex = rightDaughterIndex(nodeIndex); // opt to left+1 if (rightDtrIndex >= mNextFreeIndex) return maxIndex; double rightDtrScore = mHeap[rightDtrIndex].score(); if (rightDtrScore > maxScore) { maxIndex = rightDtrIndex; maxScore = rightDtrScore; } int grandDtr1Index = leftDaughterIndex(leftDtrIndex); if (grandDtr1Index >= mNextFreeIndex) return maxIndex; double grandDtr1Score = mHeap[grandDtr1Index].score(); if (grandDtr1Score > maxScore) { maxIndex = grandDtr1Index; maxScore = grandDtr1Score; } int grandDtr2Index = rightDaughterIndex(leftDtrIndex); if (grandDtr2Index >= mNextFreeIndex) return maxIndex; double grandDtr2Score = mHeap[grandDtr2Index].score(); if (grandDtr2Score > maxScore) { maxIndex = grandDtr2Index; maxScore = grandDtr2Score; } int grandDtr3Index = leftDaughterIndex(rightDtrIndex); if (grandDtr3Index >= mNextFreeIndex) return maxIndex; double grandDtr3Score = mHeap[grandDtr3Index].score(); if (grandDtr3Score > maxScore) { maxIndex = grandDtr3Index; maxScore = grandDtr3Score; } int grandDtr4Index = rightDaughterIndex(rightDtrIndex); if (grandDtr4Index >= mNextFreeIndex) return maxIndex; double grandDtr4Score = mHeap[grandDtr4Index].score(); return grandDtr4Score > maxScore ? grandDtr4Index : maxIndex; } boolean hasParent(int nodeIndex) { return nodeIndex > 1; } boolean noChildren(int nodeIndex) { return leftDaughterIndex(nodeIndex) >= mHeapLength; } boolean isDaughter(int nodeIndexParent, int nodeIndexDescendant) { return nodeIndexDescendant <= rightDaughterIndex(nodeIndexParent); } void swap(int index1, int index2) { E temp = mHeap[index1]; mHeap[index1] = mHeap[index2]; mHeap[index2] = temp; } static int parentIndex(int nodeIndex) { return nodeIndex / 2; // Java's int arith rounds down } static int leftDaughterIndex(int nodeIndex) { return 2 * nodeIndex; } static int rightDaughterIndex(int nodeIndex) { return 2 * nodeIndex + 1; } static void fillLevelTypes(boolean[] levelTypes) { boolean type = MAX_LEVEL; int index = 1; for (int numEltsOfType = 1;; numEltsOfType *= 2) { // 2**n per level type = !type; // reverse types at each level for (int j = 0; j < numEltsOfType; ++j) { if (index >= levelTypes.length) return; levelTypes[index++] = type; } } } static final boolean MIN_LEVEL = true; static final boolean MAX_LEVEL = false; } interface Scored { /** * Returns the score for this object. * * @return The score for this object. */ public double score(); } /** * A <code>ScoredObject</code> provides an implementation of the * <code>Scored</code> interface with an attached object. Scored * objects are immutable and identity is reference. The object * returned by the getter {@link #getObject()} is the actual object * stored, so changes to it will affect the scored object of which it * is a part. * * @author Bob Carpenter * @version 3.8.3 * @since LingPipe2.0 * @param <E> the type of object that is scored */ class ScoredObject<E> implements Scored { private final E mObj; private final double mScore; /** * Construct a scored object from the specified object * and score. * * @param obj Object for the constructed scored object. * @param score Score for the constructed scored object. */ public ScoredObject(E obj, double score) { mObj = obj; mScore = score; } /** * Returns the object attached to this scored object. * * @return The object attached to this scored object. */ public E getObject() { return mObj; } /** * Returns the score for this scored object. * * @return The score for this scored object. */ public double score() { return mScore; } /** * Returns a string-based representation of this object consisting * of the score followed by a colon (<code>':'</code>), followed * by the object converted to a string. * * @return The string-based representation of this object. */ @Override public String toString() { return mScore + ":" + getObject(); } /** * Returns <code>true</code> if the specified object is * a scored object with an object equal to this object's * and equal scores. * * @param that Object to compare to this scored object. * @return <code>true</code> if the object is a scored object * equal to this one. */ @Override @SuppressWarnings("rawtypes") // required for instanceof public boolean equals(Object that) { if (!(that instanceof ScoredObject)) return false; ScoredObject<?> thatSo = (ScoredObject<?>) that; return mObj.equals(thatSo.mObj) && mScore == thatSo.mScore; } /** * Returns a comparator that sorts in ascending order of score. * * <p>This comparator may not be consistent with equality on * the objects being compared, as it only depends on the score. * <p>The returned comparator may be used as the priority ordering * for a priority queue of objects sorted by score. It may also * be passed to {@link * java.util.Arrays#sort(Object[],Comparator)}. * * <p>This implementation is a singleton -- the same comparator * is used for all instances. * * @return The ascending score comparator. * @param <E> the type of scored objects being compared */ public static <E extends Scored> Comparator<E> comparator() { @SuppressWarnings({ "unchecked", "deprecation" }) Comparator<E> result = (Comparator<E>) SCORE_COMPARATOR; return result; } /** * Returns a comparator that sorts in descending order of score. * This is just the inverse ordering of {@link #comparator()}; see * that method's documentation for more details. * * @return The descending score comparator. * @param <E> the type of scored objects being compared */ public static <E extends Scored> Comparator<E> reverseComparator() { @SuppressWarnings({ "unchecked", "deprecation" }) Comparator<E> result = (Comparator<E>) REVERSE_SCORE_COMPARATOR; return result; } static final Comparator<Scored> REVERSE_SCORE_COMPARATOR = new ScoredObject.ReverseScoredComparator(); static final Comparator<Scored> SCORE_COMPARATOR = new ScoredObject.ScoredComparator(); // package privates can't go in interface, so park them here static class ScoredComparator implements Comparator<Scored> { public int compare(Scored obj1, Scored obj2) { return (obj1.score() > obj2.score()) ? 1 : ((obj1.score() < obj2.score()) ? -1 : 0); } }; static class ReverseScoredComparator implements Comparator<Scored> { public int compare(Scored obj1, Scored obj2) { return (obj1.score() > obj2.score()) ? -1 : ((obj1.score() < obj2.score()) ? 1 : 0); } }; }