package cs224n.util; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.List; import java.util.ArrayList; import java.io.Serializable; /** * A priority queue based on a binary heap. Note that this implementation does * not efficiently support containment, removal, or element promotion * (decreaseKey) -- these methods are therefore not yet implemented. * * @author Dan Klein */ public class PriorityQueue <E> implements Iterator<E>, Serializable, Cloneable { static final long serialVersionUID = 1L; int size; int capacity; List<E> elements; double[] priorities; protected void grow(int newCapacity) { List<E> newElements = new ArrayList<E>(newCapacity); double[] newPriorities = new double[newCapacity]; if (size > 0) { newElements.addAll(elements); System.arraycopy(priorities, 0, newPriorities, 0, priorities.length); } elements = newElements; priorities = newPriorities; capacity = newCapacity; } protected int parent(int loc) { return (loc - 1) / 2; } protected int leftChild(int loc) { return 2 * loc + 1; } protected int rightChild(int loc) { return 2 * loc + 2; } protected void heapifyUp(int loc) { if (loc == 0) return; int parent = parent(loc); if (priorities[loc] > priorities[parent]) { swap(loc, parent); heapifyUp(parent); } } protected void heapifyDown(int loc) { int max = loc; int leftChild = leftChild(loc); if (leftChild < size()) { double priority = priorities[loc]; double leftChildPriority = priorities[leftChild]; if (leftChildPriority > priority) max = leftChild; int rightChild = rightChild(loc); if (rightChild < size()) { double rightChildPriority = priorities[rightChild(loc)]; if (rightChildPriority > priority && rightChildPriority > leftChildPriority) max = rightChild; } } if (max == loc) return; swap(loc, max); heapifyDown(max); } protected void swap(int loc1, int loc2) { double tempPriority = priorities[loc1]; E tempElement = elements.get(loc1); priorities[loc1] = priorities[loc2]; elements.set(loc1, elements.get(loc2)); priorities[loc2] = tempPriority; elements.set(loc2, tempElement); } protected void removeFirst() { if (size < 1) return; swap(0, size - 1); size--; elements.remove(size); heapifyDown(0); } /** * Returns true if the priority queue is non-empty */ public boolean hasNext() { return ! isEmpty(); } /** * Returns the element in the queue with highest priority, and pops it from * the queue. */ public E next() { E first = peek(); removeFirst(); return first; } /** * Not supported -- next() already removes the head of the queue. */ public void remove() { throw new UnsupportedOperationException(); } /** * Returns the highest-priority element in the queue, but does not pop it. */ public E peek() { if (size() > 0) return elements.get(0); throw new NoSuchElementException(); } /** * Gets the priority of the highest-priority element of the queue. */ public double getPriority() { if (size() > 0) return priorities[0]; throw new NoSuchElementException(); } /** * Number of elements in the queue. */ public int size() { return size; } /** * True if the queue is empty (size == 0). */ public boolean isEmpty() { return size == 0; } /** * Adds a key to the queue with the given priority. If the key is already in * the queue, it will be added an additional time, NOT promoted/demoted. * * @param key * @param priority */ public boolean add(E key, double priority) { if (size == capacity) { grow(2 * capacity + 1); } elements.add(key); priorities[size] = priority; heapifyUp(size); size++; return true; } /** * Returns a representation of the queue in decreasing priority order. */ public String toString() { return toString(size()); } /** * Returns a representation of the queue in decreasing priority order, * displaying at most maxKeysToPring elements. * * @param maxKeysToPrint */ public String toString(int maxKeysToPrint) { PriorityQueue<E> pq = clone(); StringBuilder sb = new StringBuilder("["); int numKeysPrinted = 0; while (numKeysPrinted < maxKeysToPrint && pq.hasNext()) { double priority = pq.getPriority(); E element = pq.next(); sb.append(element.toString()); sb.append(" : "); sb.append(priority); if (numKeysPrinted < size() - 1) sb.append(", "); numKeysPrinted++; } if (numKeysPrinted < size()) sb.append("..."); sb.append("]"); return sb.toString(); } /** * Returns a counter whose keys are the elements in this priority queue, and * whose counts are the priorities in this queue. In the event there are * multiple instances of the same element in the queue, the counter's count * will be the sum of the instances' priorities. * */ public Counter<E> asCounter() { PriorityQueue<E> pq = clone(); Counter<E> counter = new Counter<E>(); while (pq.hasNext()) { double priority = pq.getPriority(); E element = pq.next(); counter.incrementCount(element, priority); } return counter; } /** * Returns a clone of this priority queue. Modifications to one will not * affect modifications to the other. */ public PriorityQueue<E> clone() { PriorityQueue<E> clonePQ = new PriorityQueue<E>(); clonePQ.size = size; clonePQ.capacity = capacity; clonePQ.elements = new ArrayList<E>(capacity); clonePQ.priorities = new double[capacity]; if (size() > 0) { clonePQ.elements.addAll(elements); System.arraycopy(priorities, 0, clonePQ.priorities, 0, size()); } return clonePQ; } public PriorityQueue() { this(15); } public PriorityQueue(int capacity) { int legalCapacity = 0; while (legalCapacity < capacity) { legalCapacity = 2 * legalCapacity + 1; } grow(legalCapacity); } public static void main(String[] args) { PriorityQueue<String> pq = new PriorityQueue<String>(); System.out.println(pq); pq.add("one",1); System.out.println(pq); pq.add("three",3); System.out.println(pq); pq.add("one",1.1); System.out.println(pq); pq.add("two",2); System.out.println(pq); System.out.println(pq.toString(2)); while (pq.hasNext()) { System.out.println(pq.next()); } } }