View Javadoc
1   /*
2    * Copyright (c) 2003, The JUNG Authors 
3    *
4    * All rights reserved.
5    *
6    * This software is open-source under the BSD license; see either
7    * "license.txt" or
8    * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
9    */
10  /*
11   * 
12   * Created on Oct 29, 2003
13   */
14  package edu.uci.ics.jung.algorithms.util;
15  
16  import java.util.AbstractCollection;
17  import java.util.Collection;
18  import java.util.Comparator;
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.Map;
22  import java.util.NoSuchElementException;
23  import java.util.Queue;
24  import java.util.Vector;
25  
26  import com.google.common.collect.Iterators;
27  
28  /**
29   * An array-based binary heap implementation of a priority queue, 
30   * which also provides
31   * efficient <code>update()</code> and <code>contains</code> operations.
32   * It contains extra infrastructure (a hash table) to keep track of the 
33   * position of each element in the array; thus, if the key value of an element
34   * changes, it may be "resubmitted" to the heap via <code>update</code>
35   * so that the heap can reposition it efficiently, as necessary.  
36   * 
37   * @author Joshua O'Madadhain
38   */
39  public class MapBinaryHeap<T>
40      extends AbstractCollection<T> 
41      implements Queue<T>
42  {
43  	private Vector<T> heap = new Vector<T>();            // holds the heap as an implicit binary tree
44      private Map<T,Integer> object_indices = new HashMap<T,Integer>(); // maps each object in the heap to its index in the heap
45      private Comparator<T> comp;
46      private final static int TOP = 0;   // the index of the top of the heap
47  
48      /**
49       * Creates a <code>MapBinaryHeap</code> whose heap ordering
50       * is based on the ordering of the elements specified by <code>comp</code>.
51       * @param comp the comparator to use to order elements in the heap
52       */
53      public MapBinaryHeap(Comparator<T> comp)
54      {
55          initialize(comp);
56      }
57      
58      /**
59       * Creates a <code>MapBinaryHeap</code> whose heap ordering
60       * will be based on the <i>natural ordering</i> of the elements,
61       * which must be <code>Comparable</code>.
62       */
63      public MapBinaryHeap()
64      {
65          initialize(new ComparableComparator());
66      }
67  
68      /**
69       * Creates a <code>MapBinaryHeap</code> based on the specified
70       * collection whose heap ordering
71       * will be based on the <i>natural ordering</i> of the elements,
72       * which must be <code>Comparable</code>.
73       * @param c the collection of {@code Comparable} elements to add to the heap
74       */
75      public MapBinaryHeap(Collection<T> c)
76      {
77      	this();
78          addAll(c);
79      }
80      
81      /**
82       * Creates a <code>MapBinaryHeap</code> based on the specified collection 
83       * whose heap ordering
84       * is based on the ordering of the elements specified by <code>c</code>.
85       * @param c the collection of elements to add to the heap
86       * @param comp the comparator to use for items in {@code c}
87       */
88      public MapBinaryHeap(Collection<T> c, Comparator<T> comp)
89      {
90          this(comp);
91          addAll(c);
92      }
93      
94      private void initialize(Comparator<T> comp)
95      {
96          this.comp = comp;
97          clear();
98      }
99      
100 	/**
101 	 * @see Collection#clear()
102 	 */
103 	@Override
104 	public void clear()
105 	{
106         object_indices.clear();
107         heap.clear();
108 	}
109 
110 	/**
111 	 * Inserts <code>o</code> into this collection.
112 	 */
113 	@Override
114 	public boolean add(T o)
115 	{
116         int i = heap.size();  // index 1 past the end of the heap
117         heap.setSize(i+1);
118         percolateUp(i, o);
119         return true;
120 	}
121 
122 	/**
123 	 * Returns <code>true</code> if this collection contains no elements, and
124      * <code>false</code> otherwise.
125 	 */
126 	@Override
127 	public boolean isEmpty()
128 	{
129         return heap.isEmpty();
130 	}
131 
132 	/**
133 	 * Returns the element at the top of the heap; does not
134      * alter the heap.
135 	 */
136 	public T peek()
137 	{
138 		if (heap.size() > 0)
139 			return heap.elementAt(TOP);
140 		else
141 			return null;
142 	}
143 
144     /**
145      * @return the size of this heap
146      */
147     @Override
148     public int size() 
149     {
150         return heap.size();
151     }
152        
153     /**
154      * Informs the heap that this object's internal key value has been
155      * updated, and that its place in the heap may need to be shifted
156      * (up or down).
157      * @param o the object whose key value has been updated
158      */
159     public void update(T o)
160     {
161         // Since we don't know whether the key value increased or 
162         // decreased, we just percolate up followed by percolating down;
163         // one of the two will have no effect.
164         
165         int cur = object_indices.get(o).intValue(); // current index
166         int new_idx = percolateUp(cur, o);
167         percolateDown(new_idx);
168     }
169 
170     @Override
171     public boolean contains(Object o)
172     {
173         return object_indices.containsKey(o);
174     }
175     
176     /**
177      * Moves the element at position <code>cur</code> closer to 
178      * the bottom of the heap, or returns if no further motion is
179      * necessary.  Calls itself recursively if further motion is 
180      * possible.
181      */
182     private void percolateDown(int cur)
183     {
184         int left = lChild(cur);
185         int right = rChild(cur);
186         int smallest;
187 
188         if ((left < heap.size()) && 
189         		(comp.compare(heap.elementAt(left), heap.elementAt(cur)) < 0)) {
190 			smallest = left;
191 		} else {
192 			smallest = cur;
193 		}
194 
195         if ((right < heap.size()) && 
196         		(comp.compare(heap.elementAt(right), heap.elementAt(smallest)) < 0)) {
197 			smallest = right;
198 		}
199 
200         if (cur != smallest)
201         {
202             swap(cur, smallest);
203             percolateDown(smallest);
204         }
205     }
206 
207     /**
208      * Moves the element <code>o</code> at position <code>cur</code> 
209      * as high as it can go in the heap.  Returns the new position of the 
210      * element in the heap.
211      */
212     private int percolateUp(int cur, T o)
213     {
214         int i = cur;
215         
216         while ((i > TOP) && (comp.compare(heap.elementAt(parent(i)), o) > 0))
217         {
218             T parentElt = heap.elementAt(parent(i));
219             heap.setElementAt(parentElt, i);
220             object_indices.put(parentElt, new Integer(i));  // reset index to i (new location)
221             i = parent(i);
222         }
223         
224         // place object in heap at appropriate place
225         object_indices.put(o, new Integer(i));
226         heap.setElementAt(o, i);
227 
228         return i;
229     }
230     
231     /**
232      * Returns the index of the left child of the element at 
233      * index <code>i</code> of the heap.
234      * @param i
235      * @return the index of the left child of the element at 
236      * index <code>i</code> of the heap
237      */
238     private int lChild(int i)
239     {
240     	return (i<<1) + 1;
241     }
242     
243     /**
244      * Returns the index of the right child of the element at 
245      * index <code>i</code> of the heap.
246      * @param i
247      * @return the index of the right child of the element at 
248      * index <code>i</code> of the heap
249      */
250     private int rChild(int i)
251     {
252     	return (i<<1) + 2;
253     }
254     
255     /**
256      * Returns the index of the parent of the element at 
257      * index <code>i</code> of the heap.
258      * @param i
259      * @return the index of the parent of the element at index i of the heap
260      */
261     private int parent(int i)
262     {
263     	return (i-1)>>1;
264     }
265     
266     /**
267      * Swaps the positions of the elements at indices <code>i</code>
268      * and <code>j</code> of the heap.
269      * @param i
270      * @param j
271      */
272     private void swap(int i, int j)
273     {
274         T iElt = heap.elementAt(i);
275         T jElt = heap.elementAt(j);
276 
277         heap.setElementAt(jElt, i);
278         object_indices.put(jElt, new Integer(i));
279 
280         heap.setElementAt(iElt, j);
281         object_indices.put(iElt, new Integer(j));
282     }
283     
284     /**
285      * Comparator used if none is specified in the constructor.
286      * @author Joshua O'Madadhain
287      */
288     private class ComparableComparator implements Comparator<T>
289     {
290         /**
291          * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
292          */
293         @SuppressWarnings("unchecked")
294         public int compare(T arg0, T arg1)
295         {
296             if (!(arg0 instanceof Comparable) || !(arg1 instanceof Comparable))
297                 throw new IllegalArgumentException("Arguments must be Comparable");
298             
299             return ((Comparable<T>)arg0).compareTo(arg1);
300         }
301     }
302 
303     /**
304      * Returns an <code>Iterator</code> that does not support modification
305      * of the heap.
306      */
307     @Override
308     public Iterator<T> iterator()
309     {
310         return Iterators.<T>unmodifiableIterator(heap.iterator());
311     }
312 
313     /**
314      * This data structure does not support the removal of arbitrary elements.
315      */
316     @Override
317     public boolean remove(Object o)
318     {
319         throw new UnsupportedOperationException();
320     }
321 
322     /**
323      * This data structure does not support the removal of arbitrary elements.
324      */
325     @Override
326     public boolean removeAll(Collection<?> c)
327     {
328         throw new UnsupportedOperationException();
329     }
330 
331     /**
332      * This data structure does not support the removal of arbitrary elements.
333      */
334     @Override
335     public boolean retainAll(Collection<?> c)
336     {
337         throw new UnsupportedOperationException();
338     }
339 
340 	public T element() throws NoSuchElementException 
341 	{
342 		T top = this.peek();
343 		if (top == null) 
344 			throw new NoSuchElementException();
345 		return top;
346 	}
347 
348 	public boolean offer(T o) 
349 	{
350 		return add(o);
351 	}
352 
353 	public T poll() 
354 	{
355         T top = this.peek();
356         if (top != null)
357         {
358 	        T bottom_elt = heap.lastElement();
359 	        heap.setElementAt(bottom_elt, TOP);
360 	        object_indices.put(bottom_elt, new Integer(TOP));
361 	        
362 	        heap.setSize(heap.size() - 1);  // remove the last element
363 	        if (heap.size() > 1)
364 	        	percolateDown(TOP);
365 	
366 	        object_indices.remove(top);
367         }
368         return top;
369 	}
370 
371 	public T remove() 
372 	{
373 		T top = this.poll();
374 		if (top == null)
375 			throw new NoSuchElementException();
376 		return top;
377 	}
378 
379 }