1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.collections;
  17. import java.util.AbstractCollection;
  18. import java.util.Comparator;
  19. import java.util.Iterator;
  20. import java.util.NoSuchElementException;
  21. /**
  22. * Binary heap implementation of <code>PriorityQueue</code>.
  23. * <p>
  24. * The <code>PriorityQueue</code> interface has now been replaced for most uses
  25. * by the <code>Buffer</code> interface. This class and the interface are
  26. * retained for backwards compatibility. The intended replacement is
  27. * {@link org.apache.commons.collections.buffer.PriorityBuffer PriorityBuffer}.
  28. * <p>
  29. * The removal order of a binary heap is based on either the natural sort
  30. * order of its elements or a specified {@link Comparator}. The
  31. * {@link #pop()} method always returns the first element as determined
  32. * by the sort order. (The <code>isMinHeap</code> flag in the constructors
  33. * can be used to reverse the sort order, in which case {@link #pop()}
  34. * will always remove the last element.) The removal order is
  35. * <i>not</i> the same as the order of iteration; elements are
  36. * returned by the iterator in no particular order.
  37. * <p>
  38. * The {@link #insert(Object)} and {@link #pop()} operations perform
  39. * in logarithmic time. The {@link #peek()} operation performs in constant
  40. * time. All other operations perform in linear time or worse.
  41. * <p>
  42. * Note that this implementation is not synchronized. Use SynchronizedPriorityQueue
  43. * to provide synchronized access to a <code>BinaryHeap</code>:
  44. *
  45. * <pre>
  46. * PriorityQueue heap = new SynchronizedPriorityQueue(new BinaryHeap());
  47. * </pre>
  48. *
  49. * @deprecated Replaced by PriorityBuffer in buffer subpackage.
  50. * Due to be removed in v4.0.
  51. * @since Commons Collections 1.0
  52. * @version $Revision: 1.24 $ $Date: 2004/02/18 01:15:42 $
  53. *
  54. * @author Peter Donald
  55. * @author Ram Chidambaram
  56. * @author Michael A. Smith
  57. * @author Paul Jack
  58. * @author Stephen Colebourne
  59. */
  60. public final class BinaryHeap extends AbstractCollection
  61. implements PriorityQueue, Buffer {
  62. /**
  63. * The default capacity for a binary heap.
  64. */
  65. private final static int DEFAULT_CAPACITY = 13;
  66. /**
  67. * The number of elements currently in this heap.
  68. */
  69. int m_size; // package scoped for testing
  70. /**
  71. * The elements in this heap.
  72. */
  73. Object[] m_elements; // package scoped for testing
  74. /**
  75. * If true, the first element as determined by the sort order will
  76. * be returned. If false, the last element as determined by the
  77. * sort order will be returned.
  78. */
  79. boolean m_isMinHeap; // package scoped for testing
  80. /**
  81. * The comparator used to order the elements
  82. */
  83. Comparator m_comparator; // package scoped for testing
  84. /**
  85. * Constructs a new minimum binary heap.
  86. */
  87. public BinaryHeap() {
  88. this(DEFAULT_CAPACITY, true);
  89. }
  90. /**
  91. * Constructs a new <code>BinaryHeap</code> that will use the given
  92. * comparator to order its elements.
  93. *
  94. * @param comparator the comparator used to order the elements, null
  95. * means use natural order
  96. */
  97. public BinaryHeap(Comparator comparator) {
  98. this();
  99. m_comparator = comparator;
  100. }
  101. /**
  102. * Constructs a new minimum binary heap with the specified initial capacity.
  103. *
  104. * @param capacity The initial capacity for the heap. This value must
  105. * be greater than zero.
  106. * @throws IllegalArgumentException
  107. * if <code>capacity</code> is <= <code>0</code>
  108. */
  109. public BinaryHeap(int capacity) {
  110. this(capacity, true);
  111. }
  112. /**
  113. * Constructs a new <code>BinaryHeap</code>.
  114. *
  115. * @param capacity the initial capacity for the heap
  116. * @param comparator the comparator used to order the elements, null
  117. * means use natural order
  118. * @throws IllegalArgumentException
  119. * if <code>capacity</code> is <= <code>0</code>
  120. */
  121. public BinaryHeap(int capacity, Comparator comparator) {
  122. this(capacity);
  123. m_comparator = comparator;
  124. }
  125. /**
  126. * Constructs a new minimum or maximum binary heap
  127. *
  128. * @param isMinHeap if <code>true</code> the heap is created as a
  129. * minimum heap; otherwise, the heap is created as a maximum heap
  130. */
  131. public BinaryHeap(boolean isMinHeap) {
  132. this(DEFAULT_CAPACITY, isMinHeap);
  133. }
  134. /**
  135. * Constructs a new <code>BinaryHeap</code>.
  136. *
  137. * @param isMinHeap true to use the order imposed by the given
  138. * comparator; false to reverse that order
  139. * @param comparator the comparator used to order the elements, null
  140. * means use natural order
  141. */
  142. public BinaryHeap(boolean isMinHeap, Comparator comparator) {
  143. this(isMinHeap);
  144. m_comparator = comparator;
  145. }
  146. /**
  147. * Constructs a new minimum or maximum binary heap with the specified
  148. * initial capacity.
  149. *
  150. * @param capacity the initial capacity for the heap. This value must
  151. * be greater than zero.
  152. * @param isMinHeap if <code>true</code> the heap is created as a
  153. * minimum heap; otherwise, the heap is created as a maximum heap.
  154. * @throws IllegalArgumentException
  155. * if <code>capacity</code> is <code><= 0</code>
  156. */
  157. public BinaryHeap(int capacity, boolean isMinHeap) {
  158. if (capacity <= 0) {
  159. throw new IllegalArgumentException("invalid capacity");
  160. }
  161. m_isMinHeap = isMinHeap;
  162. //+1 as 0 is noop
  163. m_elements = new Object[capacity + 1];
  164. }
  165. /**
  166. * Constructs a new <code>BinaryHeap</code>.
  167. *
  168. * @param capacity the initial capacity for the heap
  169. * @param isMinHeap true to use the order imposed by the given
  170. * comparator; false to reverse that order
  171. * @param comparator the comparator used to order the elements, null
  172. * means use natural order
  173. * @throws IllegalArgumentException
  174. * if <code>capacity</code> is <code><= 0</code>
  175. */
  176. public BinaryHeap(int capacity, boolean isMinHeap, Comparator comparator) {
  177. this(capacity, isMinHeap);
  178. m_comparator = comparator;
  179. }
  180. //-----------------------------------------------------------------------
  181. /**
  182. * Clears all elements from queue.
  183. */
  184. public void clear() {
  185. m_elements = new Object[m_elements.length]; // for gc
  186. m_size = 0;
  187. }
  188. /**
  189. * Tests if queue is empty.
  190. *
  191. * @return <code>true</code> if queue is empty; <code>false</code>
  192. * otherwise.
  193. */
  194. public boolean isEmpty() {
  195. return m_size == 0;
  196. }
  197. /**
  198. * Tests if queue is full.
  199. *
  200. * @return <code>true</code> if queue is full; <code>false</code>
  201. * otherwise.
  202. */
  203. public boolean isFull() {
  204. //+1 as element 0 is noop
  205. return m_elements.length == m_size + 1;
  206. }
  207. /**
  208. * Inserts an element into queue.
  209. *
  210. * @param element the element to be inserted
  211. */
  212. public void insert(Object element) {
  213. if (isFull()) {
  214. grow();
  215. }
  216. //percolate element to it's place in tree
  217. if (m_isMinHeap) {
  218. percolateUpMinHeap(element);
  219. } else {
  220. percolateUpMaxHeap(element);
  221. }
  222. }
  223. /**
  224. * Returns the element on top of heap but don't remove it.
  225. *
  226. * @return the element at top of heap
  227. * @throws NoSuchElementException if <code>isEmpty() == true</code>
  228. */
  229. public Object peek() throws NoSuchElementException {
  230. if (isEmpty()) {
  231. throw new NoSuchElementException();
  232. } else {
  233. return m_elements[1];
  234. }
  235. }
  236. /**
  237. * Returns the element on top of heap and remove it.
  238. *
  239. * @return the element at top of heap
  240. * @throws NoSuchElementException if <code>isEmpty() == true</code>
  241. */
  242. public Object pop() throws NoSuchElementException {
  243. final Object result = peek();
  244. m_elements[1] = m_elements[m_size--];
  245. // set the unused element to 'null' so that the garbage collector
  246. // can free the object if not used anywhere else.(remove reference)
  247. m_elements[m_size + 1] = null;
  248. if (m_size != 0) {
  249. // percolate top element to it's place in tree
  250. if (m_isMinHeap) {
  251. percolateDownMinHeap(1);
  252. } else {
  253. percolateDownMaxHeap(1);
  254. }
  255. }
  256. return result;
  257. }
  258. /**
  259. * Percolates element down heap from the position given by the index.
  260. * <p>
  261. * Assumes it is a minimum heap.
  262. *
  263. * @param index the index for the element
  264. */
  265. protected void percolateDownMinHeap(final int index) {
  266. final Object element = m_elements[index];
  267. int hole = index;
  268. while ((hole * 2) <= m_size) {
  269. int child = hole * 2;
  270. // if we have a right child and that child can not be percolated
  271. // up then move onto other child
  272. if (child != m_size && compare(m_elements[child + 1], m_elements[child]) < 0) {
  273. child++;
  274. }
  275. // if we found resting place of bubble then terminate search
  276. if (compare(m_elements[child], element) >= 0) {
  277. break;
  278. }
  279. m_elements[hole] = m_elements[child];
  280. hole = child;
  281. }
  282. m_elements[hole] = element;
  283. }
  284. /**
  285. * Percolates element down heap from the position given by the index.
  286. * <p>
  287. * Assumes it is a maximum heap.
  288. *
  289. * @param index the index of the element
  290. */
  291. protected void percolateDownMaxHeap(final int index) {
  292. final Object element = m_elements[index];
  293. int hole = index;
  294. while ((hole * 2) <= m_size) {
  295. int child = hole * 2;
  296. // if we have a right child and that child can not be percolated
  297. // up then move onto other child
  298. if (child != m_size && compare(m_elements[child + 1], m_elements[child]) > 0) {
  299. child++;
  300. }
  301. // if we found resting place of bubble then terminate search
  302. if (compare(m_elements[child], element) <= 0) {
  303. break;
  304. }
  305. m_elements[hole] = m_elements[child];
  306. hole = child;
  307. }
  308. m_elements[hole] = element;
  309. }
  310. /**
  311. * Percolates element up heap from the position given by the index.
  312. * <p>
  313. * Assumes it is a minimum heap.
  314. *
  315. * @param index the index of the element to be percolated up
  316. */
  317. protected void percolateUpMinHeap(final int index) {
  318. int hole = index;
  319. Object element = m_elements[hole];
  320. while (hole > 1 && compare(element, m_elements[hole / 2]) < 0) {
  321. // save element that is being pushed down
  322. // as the element "bubble" is percolated up
  323. final int next = hole / 2;
  324. m_elements[hole] = m_elements[next];
  325. hole = next;
  326. }
  327. m_elements[hole] = element;
  328. }
  329. /**
  330. * Percolates a new element up heap from the bottom.
  331. * <p>
  332. * Assumes it is a minimum heap.
  333. *
  334. * @param element the element
  335. */
  336. protected void percolateUpMinHeap(final Object element) {
  337. m_elements[++m_size] = element;
  338. percolateUpMinHeap(m_size);
  339. }
  340. /**
  341. * Percolates element up heap from from the position given by the index.
  342. * <p>
  343. * Assume it is a maximum heap.
  344. *
  345. * @param index the index of the element to be percolated up
  346. */
  347. protected void percolateUpMaxHeap(final int index) {
  348. int hole = index;
  349. Object element = m_elements[hole];
  350. while (hole > 1 && compare(element, m_elements[hole / 2]) > 0) {
  351. // save element that is being pushed down
  352. // as the element "bubble" is percolated up
  353. final int next = hole / 2;
  354. m_elements[hole] = m_elements[next];
  355. hole = next;
  356. }
  357. m_elements[hole] = element;
  358. }
  359. /**
  360. * Percolates a new element up heap from the bottom.
  361. * <p>
  362. * Assume it is a maximum heap.
  363. *
  364. * @param element the element
  365. */
  366. protected void percolateUpMaxHeap(final Object element) {
  367. m_elements[++m_size] = element;
  368. percolateUpMaxHeap(m_size);
  369. }
  370. /**
  371. * Compares two objects using the comparator if specified, or the
  372. * natural order otherwise.
  373. *
  374. * @param a the first object
  375. * @param b the second object
  376. * @return -ve if a less than b, 0 if they are equal, +ve if a greater than b
  377. */
  378. private int compare(Object a, Object b) {
  379. if (m_comparator != null) {
  380. return m_comparator.compare(a, b);
  381. } else {
  382. return ((Comparable) a).compareTo(b);
  383. }
  384. }
  385. /**
  386. * Increases the size of the heap to support additional elements
  387. */
  388. protected void grow() {
  389. final Object[] elements = new Object[m_elements.length * 2];
  390. System.arraycopy(m_elements, 0, elements, 0, m_elements.length);
  391. m_elements = elements;
  392. }
  393. /**
  394. * Returns a string representation of this heap. The returned string
  395. * is similar to those produced by standard JDK collections.
  396. *
  397. * @return a string representation of this heap
  398. */
  399. public String toString() {
  400. final StringBuffer sb = new StringBuffer();
  401. sb.append("[ ");
  402. for (int i = 1; i < m_size + 1; i++) {
  403. if (i != 1) {
  404. sb.append(", ");
  405. }
  406. sb.append(m_elements[i]);
  407. }
  408. sb.append(" ]");
  409. return sb.toString();
  410. }
  411. /**
  412. * Returns an iterator over this heap's elements.
  413. *
  414. * @return an iterator over this heap's elements
  415. */
  416. public Iterator iterator() {
  417. return new Iterator() {
  418. private int index = 1;
  419. private int lastReturnedIndex = -1;
  420. public boolean hasNext() {
  421. return index <= m_size;
  422. }
  423. public Object next() {
  424. if (!hasNext()) throw new NoSuchElementException();
  425. lastReturnedIndex = index;
  426. index++;
  427. return m_elements[lastReturnedIndex];
  428. }
  429. public void remove() {
  430. if (lastReturnedIndex == -1) {
  431. throw new IllegalStateException();
  432. }
  433. m_elements[ lastReturnedIndex ] = m_elements[ m_size ];
  434. m_elements[ m_size ] = null;
  435. m_size--;
  436. if( m_size != 0 && lastReturnedIndex <= m_size) {
  437. int compareToParent = 0;
  438. if (lastReturnedIndex > 1) {
  439. compareToParent = compare(m_elements[lastReturnedIndex],
  440. m_elements[lastReturnedIndex / 2]);
  441. }
  442. if (m_isMinHeap) {
  443. if (lastReturnedIndex > 1 && compareToParent < 0) {
  444. percolateUpMinHeap(lastReturnedIndex);
  445. } else {
  446. percolateDownMinHeap(lastReturnedIndex);
  447. }
  448. } else { // max heap
  449. if (lastReturnedIndex > 1 && compareToParent > 0) {
  450. percolateUpMaxHeap(lastReturnedIndex);
  451. } else {
  452. percolateDownMaxHeap(lastReturnedIndex);
  453. }
  454. }
  455. }
  456. index--;
  457. lastReturnedIndex = -1;
  458. }
  459. };
  460. }
  461. /**
  462. * Adds an object to this heap. Same as {@link #insert(Object)}.
  463. *
  464. * @param object the object to add
  465. * @return true, always
  466. */
  467. public boolean add(Object object) {
  468. insert(object);
  469. return true;
  470. }
  471. /**
  472. * Returns the priority element. Same as {@link #peek()}.
  473. *
  474. * @return the priority element
  475. * @throws BufferUnderflowException if this heap is empty
  476. */
  477. public Object get() {
  478. try {
  479. return peek();
  480. } catch (NoSuchElementException e) {
  481. throw new BufferUnderflowException();
  482. }
  483. }
  484. /**
  485. * Removes the priority element. Same as {@link #pop()}.
  486. *
  487. * @return the removed priority element
  488. * @throws BufferUnderflowException if this heap is empty
  489. */
  490. public Object remove() {
  491. try {
  492. return pop();
  493. } catch (NoSuchElementException e) {
  494. throw new BufferUnderflowException();
  495. }
  496. }
  497. /**
  498. * Returns the number of elements in this heap.
  499. *
  500. * @return the number of elements in this heap
  501. */
  502. public int size() {
  503. return m_size;
  504. }
  505. }