All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.stanford.nlp.util.ArrayHeap Maven / Gradle / Ivy

package edu.stanford.nlp.util;

import java.util.*;

/**
 * Implements a heap as an ArrayList.
 * Values are all implicit in the comparator
 * passed in on construction.  Decrease key is supported, though only
 * lg(n).  Unlike the previous implementation of this class, this
 * heap interprets the addition of an existing element as a "change
 * key" which gets ignored unless it actually turns out to be a
 * decrease key.  Note that in this implementation, changing the key
 * of an object should trigger a change in the comparator's ordering
 * for that object, but should NOT change the equality of that
 * object.
 *
 * @author Dan Klein
 * @author Christopher Manning
 * @version 1.2, 07/31/02
 */
public class ArrayHeap extends AbstractSet implements Heap {

  /**
   * A HeapEntry stores an object in the heap along with
   * its current location (array position) in the heap.
   *
   * @author Dan Klein
   * @version 1.2
   */
  private static final class HeapEntry {
    public E object;
    public int index;
  }

  /**
   * indexToEntry maps linear array locations (not
   * priorities) to heap entries.
   */
  private ArrayList> indexToEntry;
  /**
   * objectToEntry maps heap objects to their heap
   * entries.
   */
  private Map> objectToEntry;
  /**
   * cmp is the comparator passed on construction.
   */
  private Comparator cmp;

  // Primitive Heap Operations

  private static int parent(final int index) {
    return (index - 1) / 2;
  }

  private HeapEntry parent(HeapEntry entry) {
    int index = entry.index;
    return (index > 0 ? indexToEntry.get((index - 1) / 2) : null);
  }

  private HeapEntry leftChild(HeapEntry entry) {
    int index = entry.index;
    int leftIndex = index * 2 + 1;
    return (leftIndex < size() ? indexToEntry.get(leftIndex) : null);
  }

  private HeapEntry rightChild(HeapEntry entry) {
    int index = entry.index;
    int rightIndex = index * 2 + 2;
    return (rightIndex < size() ? indexToEntry.get(rightIndex) : null);
  }

  private int compare(HeapEntry entryA, HeapEntry entryB) {
    return cmp.compare(entryA.object, entryB.object);
  }

  private void swap(HeapEntry entryA, HeapEntry entryB) {
    int indexA = entryA.index;
    int indexB = entryB.index;
    entryA.index = indexB;
    entryB.index = indexA;
    indexToEntry.set(indexA, entryB);
    indexToEntry.set(indexB, entryA);
  }

  /**
   * Remove the last element of the heap (last in the index array).
   * Do not call this on other entries; the last entry is only passed
   * in for efficiency.
   *
   * @param entry the last entry in the array
   */
  private void removeLast(HeapEntry entry) {
    indexToEntry.remove(entry.index);
    objectToEntry.remove(entry.object);
  }

  private HeapEntry getEntry(E o) {
    HeapEntry entry = objectToEntry.get(o);
    if (entry == null) {
      entry = new HeapEntry();
      entry.index = size();
      entry.object = o;
      indexToEntry.add(entry);
      objectToEntry.put(o, entry);
    }
    return entry;
  }

  /**
   * iterative heapify up: move item o at index up until correctly placed
   */
  private int heapifyUp(HeapEntry entry) {
    int numSwaps = 0;
    while (true) {
      if (entry.index == 0) {
        break;
      }
      HeapEntry parentEntry = parent(entry);
      if (compare(entry, parentEntry) >= 0) {
        break;
      }
      numSwaps++;
      swap(entry, parentEntry);
    }
    return numSwaps;
  }

  /**
   * On the assumption that
   * leftChild(entry) and rightChild(entry) satisfy the heap property,
   * make sure that the heap at entry satisfies this property by possibly
   * percolating the element o downwards.  I've replaced the obvious
   * recursive formulation with an iterative one to gain (marginal) speed
   */
  private void heapifyDown(HeapEntry entry) {
    // int size = size();

    HeapEntry minEntry; // = null;

    do {
      minEntry = entry;

      HeapEntry leftEntry = leftChild(entry);
      if (leftEntry != null) {
        if (compare(minEntry, leftEntry) > 0) {
          minEntry = leftEntry;
        }
      }

      HeapEntry rightEntry = rightChild(entry);
      if (rightEntry != null) {
        if (compare(minEntry, rightEntry) > 0) {
          minEntry = rightEntry;
        }
      }

      if (minEntry != entry) {
        // Swap min and current
        swap(minEntry, entry);
        // at start of next loop, we set currentIndex to largestIndex
        // this indexation now holds current, so it is unchanged
      }
    } while (minEntry != entry);
    // System.err.println("Done with heapify down");
    // verify();
  }


  /**
   * Finds the object with the minimum key, removes it from the heap,
   * and returns it.
   *
   * @return The object with minimum key
   */
  @Override
  public E extractMin() {
    if (isEmpty()) {
      throw new NoSuchElementException();
    }
    HeapEntry minEntry = indexToEntry.get(0);
    int lastIndex = size() - 1;
    if (lastIndex > 0) {
      HeapEntry lastEntry =  indexToEntry.get(lastIndex);
      swap(lastEntry, minEntry);
      removeLast(minEntry);
      heapifyDown(lastEntry);
    } else {
      removeLast(minEntry);
    }
    return minEntry.object;
  }

  /**
   * Finds the object with the minimum key and returns it, without
   * modifying the heap.
   *
   * @return The object with minimum key
   */
  @Override
  public E min() {
    HeapEntry minEntry = indexToEntry.get(0);
    return minEntry.object;
  }

  /**
   * Adds an object to the heap.  If the object is already in the heap
   * with worse score, this acts as a decrease key.  If the object is
   * already present, with better score, it will NOT cause an
   * "increase key".
   *
   * @param o an Object value
   */
  @Override
  public boolean add(E o) {
    decreaseKey(o);
    return true;
  }

  /**
   * Changes the position of an element o in the heap based on a
   * change in the ordering of o.  If o's key has actually increased,
   * it will do nothing, particularly not an "increase key".
   *
   * @param o An Object value
   * @return The number of swaps done on decrease.
   */
  @Override
  public int decreaseKey(E o) {
    HeapEntry entry = getEntry(o);
    if (o != entry.object) {
      if (cmp.compare(o, entry.object) < 0) {
        entry.object = o;
      }
    }
    return heapifyUp(entry);
  }

  /**
   * Checks if the heap is empty.
   *
   * @return a boolean value
   */
  @Override
  public boolean isEmpty() {
    return indexToEntry.isEmpty();
  }

  /**
   * Get the number of elements in the heap.
   *
   * @return an int value
   */
  @Override
  public int size() {
    return indexToEntry.size();
  }

  @Override
  public Iterator iterator() {
    Heap tempHeap = new ArrayHeap(cmp, size());
    List tempList = new ArrayList(size());
    for (E obj : objectToEntry.keySet()) {
      tempHeap.add(obj);
    }
    while (!tempHeap.isEmpty()) {
      tempList.add(tempHeap.extractMin());
    }
    return tempList.iterator();
  }

  /**
   * Clears the heap.  Equivalent to calling extractMin repeatedly
   * (but faster).
   */
  @Override
  public void clear() {
    indexToEntry.clear();
    objectToEntry.clear();
  }

  public void dump() {
    for (int j = 0; j < indexToEntry.size(); j++) {
      System.err.println(" " + j + " " + ((Scored) indexToEntry.get(j).object).score());
    }
  }

  public void verify() {
    for (int i = 0; i < indexToEntry.size(); i++) {
      if (i != 0) {
        // check ordering
        if (compare(indexToEntry.get(i), indexToEntry.get(parent(i))) < 0) {
          System.err.println("Error in the ordering of the heap! (" + i + ")");
          dump();
          System.exit(0);
        }
      }
      // check placement
      if (i != indexToEntry.get(i).index) {
        System.err.println("Error in placement in the heap!");
      }
    }
  }

  /** Create an ArrayHeap.
   *
   *  @param cmp The objects added will be ordered using the Comparator.
   */
  public ArrayHeap(Comparator cmp) {
    this.cmp = cmp;
    indexToEntry = new ArrayList>();
    objectToEntry = Generics.newHashMap();
  }

  public ArrayHeap(Comparator cmp, int initCapacity) {
    this.cmp = cmp;
    indexToEntry = new ArrayList>(initCapacity);
    objectToEntry = Generics.newHashMap(initCapacity);
  }

  public List asList() {
    return new LinkedList(this);
  }

  /**
   * Prints the array entries in sorted comparator order.
   * @return The array entries in sorted comparator order.
   */
  @Override
  public String toString() {
    ArrayList result = new ArrayList();
    for(E key : objectToEntry.keySet())
      result.add(key);
    Collections.sort(result,cmp);
    return result.toString();
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy