BinaryHeapPriorityQueue.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. package edu.stanford.nlp.util;
  2. import java.util.*;
  3. /**
  4. * PriorityQueue with explicit double priority values. Larger doubles are higher priorities. BinaryHeap-backed.
  5. *
  6. * @author Dan Klein
  7. * @author Christopher Manning
  8. * For each entry, uses ~ 24 (entry) + 16? (Map.Entry) + 4 (List entry) = 44 bytes?
  9. */
  10. public class BinaryHeapPriorityQueue<E> extends AbstractSet<E> implements PriorityQueue<E>, Iterator<E> {
  11. /**
  12. * An <code>Entry</code> stores an object in the queue along with
  13. * its current location (array position) and priority.
  14. * uses ~ 8 (self) + 4 (key ptr) + 4 (index) + 8 (priority) = 24 bytes?
  15. */
  16. private static final class Entry<E> {
  17. public E key;
  18. public int index;
  19. public double priority;
  20. @Override
  21. public String toString() {
  22. return key + " at " + index + " (" + priority + ")";
  23. }
  24. }
  25. public boolean hasNext() {
  26. return size() > 0;
  27. }
  28. public E next() {
  29. return removeFirst();
  30. }
  31. public void remove() {
  32. throw new UnsupportedOperationException();
  33. }
  34. /**
  35. * <code>indexToEntry</code> maps linear array locations (not
  36. * priorities) to heap entries.
  37. */
  38. private List<Entry<E>> indexToEntry;
  39. /**
  40. * <code>keyToEntry</code> maps heap objects to their heap
  41. * entries.
  42. */
  43. private Map<Object,Entry<E>> keyToEntry;
  44. private Entry<E> parent(Entry<E> entry) {
  45. int index = entry.index;
  46. return (index > 0 ? getEntry((index - 1) / 2) : null);
  47. }
  48. private Entry<E> leftChild(Entry<E> entry) {
  49. int leftIndex = entry.index * 2 + 1;
  50. return (leftIndex < size() ? getEntry(leftIndex) : null);
  51. }
  52. private Entry<E> rightChild(Entry<E> entry) {
  53. int index = entry.index;
  54. int rightIndex = index * 2 + 2;
  55. return (rightIndex < size() ? getEntry(rightIndex) : null);
  56. }
  57. private int compare(Entry<E> entryA, Entry<E> entryB) {
  58. return compare(entryA.priority, entryB.priority);
  59. }
  60. private static int compare(double a, double b) {
  61. double diff = a - b;
  62. if (diff > 0.0) {
  63. return 1;
  64. }
  65. if (diff < 0.0) {
  66. return -1;
  67. }
  68. return 0;
  69. }
  70. /**
  71. * Structural swap of two entries.
  72. *
  73. * @param entryA
  74. * @param entryB
  75. */
  76. private void swap(Entry<E> entryA, Entry<E> entryB) {
  77. int indexA = entryA.index;
  78. int indexB = entryB.index;
  79. entryA.index = indexB;
  80. entryB.index = indexA;
  81. indexToEntry.set(indexA, entryB);
  82. indexToEntry.set(indexB, entryA);
  83. }
  84. /**
  85. * Remove the last element of the heap (last in the index array).
  86. */
  87. private void removeLastEntry() {
  88. Entry<E> entry = indexToEntry.remove(size() - 1);
  89. keyToEntry.remove(entry.key);
  90. }
  91. /**
  92. * Get the entry by key (null if none).
  93. */
  94. private Entry<E> getEntry(E key) {
  95. return keyToEntry.get(key);
  96. }
  97. /**
  98. * Get entry by index, exception if none.
  99. */
  100. private Entry<E> getEntry(int index) {
  101. Entry<E> entry = indexToEntry.get(index);
  102. return entry;
  103. }
  104. private Entry<E> makeEntry(E key) {
  105. Entry<E> entry = new Entry<E>();
  106. entry.index = size();
  107. entry.key = key;
  108. entry.priority = Double.NEGATIVE_INFINITY;
  109. indexToEntry.add(entry);
  110. keyToEntry.put(key, entry);
  111. return entry;
  112. }
  113. /**
  114. * iterative heapify up: move item o at index up until correctly placed
  115. */
  116. private void heapifyUp(Entry<E> entry) {
  117. while (true) {
  118. if (entry.index == 0) {
  119. break;
  120. }
  121. Entry<E> parentEntry = parent(entry);
  122. if (compare(entry, parentEntry) <= 0) {
  123. break;
  124. }
  125. swap(entry, parentEntry);
  126. }
  127. }
  128. /**
  129. * On the assumption that
  130. * leftChild(entry) and rightChild(entry) satisfy the heap property,
  131. * make sure that the heap at entry satisfies this property by possibly
  132. * percolating the element o downwards. I've replaced the obvious
  133. * recursive formulation with an iterative one to gain (marginal) speed
  134. */
  135. private void heapifyDown(Entry<E> entry) {
  136. Entry<E> currentEntry = entry;
  137. Entry<E> bestEntry; // initialized below
  138. do {
  139. bestEntry = currentEntry;
  140. Entry<E> leftEntry = leftChild(currentEntry);
  141. if (leftEntry != null) {
  142. if (compare(bestEntry, leftEntry) < 0) {
  143. bestEntry = leftEntry;
  144. }
  145. }
  146. Entry<E> rightEntry = rightChild(currentEntry);
  147. if (rightEntry != null) {
  148. if (compare(bestEntry, rightEntry) < 0) {
  149. bestEntry = rightEntry;
  150. }
  151. }
  152. if (bestEntry != currentEntry) {
  153. // Swap min and current
  154. swap(bestEntry, currentEntry);
  155. // at start of next loop, we set currentIndex to largestIndex
  156. // this indexation now holds current, so it is unchanged
  157. }
  158. } while (bestEntry != currentEntry);
  159. // System.err.println("Done with heapify down");
  160. // verify();
  161. }
  162. private void heapify(Entry<E> entry) {
  163. heapifyUp(entry);
  164. heapifyDown(entry);
  165. }
  166. /**
  167. * Finds the object with the highest priority, removes it,
  168. * and returns it.
  169. *
  170. * @return the object with highest priority
  171. */
  172. public E removeFirst() {
  173. E first = getFirst();
  174. remove(first);
  175. return first;
  176. }
  177. /**
  178. * Finds the object with the highest priority and returns it, without
  179. * modifying the queue.
  180. *
  181. * @return the object with minimum key
  182. */
  183. public E getFirst() {
  184. if (isEmpty()) {
  185. throw new NoSuchElementException();
  186. }
  187. return getEntry(0).key;
  188. }
  189. /**
  190. * Gets the priority of the highest-priority element of the queue.
  191. */
  192. public double getPriority() {
  193. if (isEmpty()) {
  194. throw new NoSuchElementException();
  195. }
  196. return getEntry(0).priority;
  197. }
  198. /**
  199. * Searches for the object in the queue and returns it. May be useful if
  200. * you can create a new object that is .equals() to an object in the queue
  201. * but is not actually identical, or if you want to modify an object that is
  202. * in the queue.
  203. * @return null if the object is not in the queue, otherwise returns the
  204. * object.
  205. */
  206. public E getObject(E key) {
  207. if ( ! contains(key)) return null;
  208. Entry<E> e = getEntry(key);
  209. return e.key;
  210. }
  211. /**
  212. * Get the priority of a key -- if the key is not in the queue, Double.NEGATIVE_INFINITY is returned.
  213. *
  214. * @param key
  215. * @return
  216. */
  217. public double getPriority(E key) {
  218. Entry<E> entry = getEntry(key);
  219. if (entry == null) {
  220. return Double.NEGATIVE_INFINITY;
  221. }
  222. return entry.priority;
  223. }
  224. /**
  225. * Adds an object to the queue with the minimum priority
  226. * (Double.NEGATIVE_INFINITY). If the object is already in the queue
  227. * with worse priority, this does nothing. If the object is
  228. * already present, with better priority, it will NOT cause an
  229. * a decreasePriority.
  230. *
  231. * @param key an <code>Object</code> value
  232. * @return whether the key was present before
  233. */
  234. @Override
  235. public boolean add(E key) {
  236. if (contains(key)) {
  237. return false;
  238. }
  239. makeEntry(key);
  240. return true;
  241. }
  242. /**
  243. * Convenience method for if you want to pretend relaxPriority doesn't exist, or if you really want add's return conditions.
  244. */
  245. public boolean add(E key, double priority) {
  246. // System.err.println("Adding " + key + " with priority " + priority);
  247. if (add(key)) {
  248. relaxPriority(key, priority);
  249. return true;
  250. }
  251. return false;
  252. }
  253. @Override
  254. public boolean remove(Object key) {
  255. E eKey = (E) key;
  256. Entry<E> entry = getEntry(eKey);
  257. if (entry == null) {
  258. return false;
  259. }
  260. removeEntry(entry);
  261. return true;
  262. }
  263. private void removeEntry(Entry<E> entry) {
  264. Entry<E> lastEntry = getLastEntry();
  265. if (entry != lastEntry) {
  266. swap(entry, lastEntry);
  267. removeLastEntry();
  268. heapify(lastEntry);
  269. } else {
  270. removeLastEntry();
  271. }
  272. }
  273. private Entry<E> getLastEntry() {
  274. return getEntry(size() - 1);
  275. }
  276. /**
  277. * Promotes a key in the queue, adding it if it wasn't there already. If the specified priority is worse than the current priority, nothing happens. Faster than add if you don't care about whether the key is new.
  278. *
  279. * @param key an <code>Object</code> value
  280. * @return whether the priority actually improved.
  281. */
  282. public boolean relaxPriority(E key, double priority) {
  283. Entry<E> entry = getEntry(key);
  284. if (entry == null) {
  285. entry = makeEntry(key);
  286. }
  287. if (compare(priority, entry.priority) <= 0) {
  288. return false;
  289. }
  290. entry.priority = priority;
  291. heapifyUp(entry);
  292. return true;
  293. }
  294. /**
  295. * Demotes a key in the queue, adding it if it wasn't there already. If the specified priority is better than the current priority, nothing happens. If you decrease the priority on a non-present key, it will get added, but at it's old implicit priority of Double.NEGATIVE_INFINITY.
  296. *
  297. * @param key an <code>Object</code> value
  298. * @return whether the priority actually improved.
  299. */
  300. public boolean decreasePriority(E key, double priority) {
  301. Entry<E> entry = getEntry(key);
  302. if (entry == null) {
  303. entry = makeEntry(key);
  304. }
  305. if (compare(priority, entry.priority) >= 0) {
  306. return false;
  307. }
  308. entry.priority = priority;
  309. heapifyDown(entry);
  310. return true;
  311. }
  312. /**
  313. * Changes a priority, either up or down, adding the key it if it wasn't there already.
  314. *
  315. * @param key an <code>Object</code> value
  316. * @return whether the priority actually changed.
  317. */
  318. public boolean changePriority(E key, double priority) {
  319. Entry<E> entry = getEntry(key);
  320. if (entry == null) {
  321. entry = makeEntry(key);
  322. }
  323. if (compare(priority, entry.priority) == 0) {
  324. return false;
  325. }
  326. entry.priority = priority;
  327. heapify(entry);
  328. return true;
  329. }
  330. /**
  331. * Checks if the queue is empty.
  332. *
  333. * @return a <code>boolean</code> value
  334. */
  335. @Override
  336. public boolean isEmpty() {
  337. return indexToEntry.isEmpty();
  338. }
  339. /**
  340. * Get the number of elements in the queue.
  341. *
  342. * @return queue size
  343. */
  344. @Override
  345. public int size() {
  346. return indexToEntry.size();
  347. }
  348. /**
  349. * Returns whether the queue contains the given key.
  350. */
  351. @Override
  352. public boolean contains(Object key) {
  353. return keyToEntry.containsKey(key);
  354. }
  355. public List<E> toSortedList() {
  356. List<E> sortedList = new ArrayList<E>(size());
  357. BinaryHeapPriorityQueue<E> queue = this.deepCopy();
  358. while (!queue.isEmpty()) {
  359. sortedList.add(queue.removeFirst());
  360. }
  361. return sortedList;
  362. }
  363. public BinaryHeapPriorityQueue<E> deepCopy(MapFactory<Object, Entry<E>> mapFactory) {
  364. BinaryHeapPriorityQueue<E> queue =
  365. new BinaryHeapPriorityQueue<E>(mapFactory);
  366. for (Entry<E> entry : keyToEntry.values()) {
  367. queue.relaxPriority(entry.key, entry.priority);
  368. }
  369. return queue;
  370. }
  371. public BinaryHeapPriorityQueue<E> deepCopy() {
  372. return deepCopy(MapFactory.HASH_MAP_FACTORY);
  373. }
  374. @Override
  375. public Iterator<E> iterator() {
  376. return Collections.unmodifiableCollection(toSortedList()).iterator();
  377. }
  378. /**
  379. * Clears the queue.
  380. */
  381. @Override
  382. public void clear() {
  383. indexToEntry.clear();
  384. keyToEntry.clear();
  385. }
  386. // private void verify() {
  387. // for (int i = 0; i < indexToEntry.size(); i++) {
  388. // if (i != 0) {
  389. // // check ordering
  390. // if (compare(getEntry(i), parent(getEntry(i))) < 0) {
  391. // System.err.println("Error in the ordering of the heap! ("+i+")");
  392. // System.exit(0);
  393. // }
  394. // }
  395. // // check placement
  396. // if (i != ((Entry)indexToEntry.get(i)).index)
  397. // System.err.println("Error in placement in the heap!");
  398. // }
  399. // }
  400. @Override
  401. public String toString() {
  402. return toString(0);
  403. }
  404. /** {@inheritDoc} */
  405. public String toString(int maxKeysToPrint) {
  406. if (maxKeysToPrint <= 0) maxKeysToPrint = Integer.MAX_VALUE;
  407. List<E> sortedKeys = toSortedList();
  408. StringBuilder sb = new StringBuilder("[");
  409. for (int i = 0; i < maxKeysToPrint && i < sortedKeys.size(); i++) {
  410. E key = sortedKeys.get(i);
  411. sb.append(key).append("=").append(getPriority(key));
  412. if (i < maxKeysToPrint - 1 && i < sortedKeys.size() - 1) {
  413. sb.append(", ");
  414. }
  415. }
  416. sb.append("]");
  417. return sb.toString();
  418. }
  419. public String toVerticalString() {
  420. List<E> sortedKeys = toSortedList();
  421. StringBuilder sb = new StringBuilder();
  422. for (Iterator<E> keyI = sortedKeys.iterator(); keyI.hasNext();) {
  423. E key = keyI.next();
  424. sb.append(key);
  425. sb.append("\t");
  426. sb.append(getPriority(key));
  427. if (keyI.hasNext()) {
  428. sb.append("\n");
  429. }
  430. }
  431. return sb.toString();
  432. }
  433. public BinaryHeapPriorityQueue() {
  434. this(MapFactory.HASH_MAP_FACTORY);
  435. }
  436. public BinaryHeapPriorityQueue(MapFactory<Object, Entry<E>> mapFactory) {
  437. indexToEntry = new ArrayList<Entry<E>>();
  438. keyToEntry = mapFactory.newMap();
  439. }
  440. public static void main(String[] args) {
  441. BinaryHeapPriorityQueue<String> queue =
  442. new BinaryHeapPriorityQueue<String>();
  443. queue.add("a", 1.0);
  444. System.out.println("Added a:1 " + queue);
  445. queue.add("b", 2.0);
  446. System.out.println("Added b:2 " + queue);
  447. queue.add("c", 1.5);
  448. System.out.println("Added c:1.5 " + queue);
  449. queue.relaxPriority("a", 3.0);
  450. System.out.println("Increased a to 3 " + queue);
  451. queue.decreasePriority("b", 0.0);
  452. System.out.println("Decreased b to 0 " + queue);
  453. System.out.println("removeFirst()=" + queue.removeFirst());
  454. System.out.println("queue=" + queue);
  455. System.out.println("removeFirst()=" + queue.removeFirst());
  456. System.out.println("queue=" + queue);
  457. System.out.println("removeFirst()=" + queue.removeFirst());
  458. System.out.println("queue=" + queue);
  459. }
  460. }