/*
* Copyright (c) 2003, the JUNG Project and the Regents of the University
* of California
* All rights reserved.
*
* This software is open-source under the BSD license; see either
* "license.txt" or
* http://jung.sourceforge.net/license.txt for a description.
*/
/*
*
* Created on Oct 29, 2003
*/
package edu.uci.ics.jung.algorithms.util;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Vector;
import org.apache.commons.collections15.IteratorUtils;
/**
* An array-based binary heap implementation of a priority queue,
* which also provides
* efficient update()
and contains
operations.
* It contains extra infrastructure (a hash table) to keep track of the
* position of each element in the array; thus, if the key value of an element
* changes, it may be "resubmitted" to the heap via update
* so that the heap can reposition it efficiently, as necessary.
*
* @author Joshua O'Madadhain
*/
public class MapBinaryHeap
extends AbstractCollection
implements Queue
{
private Vector heap = new Vector(); // holds the heap as an implicit binary tree
private Map object_indices = new HashMap(); // maps each object in the heap to its index in the heap
private Comparator comp;
private final static int TOP = 0; // the index of the top of the heap
/**
* Creates a MapBinaryHeap
whose heap ordering
* is based on the ordering of the elements specified by c
.
*/
public MapBinaryHeap(Comparator comp)
{
initialize(comp);
}
/**
* Creates a MapBinaryHeap
whose heap ordering
* will be based on the natural ordering of the elements,
* which must be Comparable
.
*/
public MapBinaryHeap()
{
initialize(new ComparableComparator());
}
/**
* Creates a MapBinaryHeap
based on the specified
* collection whose heap ordering
* will be based on the natural ordering of the elements,
* which must be Comparable
.
*/
public MapBinaryHeap(Collection c)
{
this();
addAll(c);
}
/**
* Creates a MapBinaryHeap
based on the specified collection
* whose heap ordering
* is based on the ordering of the elements specified by c
.
*/
public MapBinaryHeap(Collection c, Comparator comp)
{
this(comp);
addAll(c);
}
private void initialize(Comparator comp)
{
this.comp = comp;
clear();
}
/**
* @see Collection#clear()
*/
@Override
public void clear()
{
object_indices.clear();
heap.clear();
}
/**
* Inserts o
into this collection.
*/
@Override
public boolean add(T o)
{
int i = heap.size(); // index 1 past the end of the heap
heap.setSize(i+1);
percolateUp(i, o);
return true;
}
/**
* Returns true
if this collection contains no elements, and
* false
otherwise.
*/
@Override
public boolean isEmpty()
{
return heap.isEmpty();
}
/**
* Returns the element at the top of the heap; does not
* alter the heap.
*/
public T peek()
{
if (heap.size() > 0)
return heap.elementAt(TOP);
else
return null;
}
/**
* Removes the element at the top of this heap, and returns it.
* @deprecated Use {@link MapBinaryHeap#poll()}
* or {@link MapBinaryHeap#remove()} instead.
*/
@Deprecated
public T pop() throws NoSuchElementException
{
return this.remove();
}
/**
* Returns the size of this heap.
*/
@Override
public int size()
{
return heap.size();
}
/**
* Informs the heap that this object's internal key value has been
* updated, and that its place in the heap may need to be shifted
* (up or down).
* @param o
*/
public void update(T o)
{
// Since we don't know whether the key value increased or
// decreased, we just percolate up followed by percolating down;
// one of the two will have no effect.
int cur = object_indices.get(o).intValue(); // current index
int new_idx = percolateUp(cur, o);
percolateDown(new_idx);
}
/**
* @see Collection#contains(java.lang.Object)
*/
@Override
public boolean contains(Object o)
{
return object_indices.containsKey(o);
}
/**
* Moves the element at position cur
closer to
* the bottom of the heap, or returns if no further motion is
* necessary. Calls itself recursively if further motion is
* possible.
*/
private void percolateDown(int cur)
{
int left = lChild(cur);
int right = rChild(cur);
int smallest;
if ((left < heap.size()) &&
(comp.compare(heap.elementAt(left), heap.elementAt(cur)) < 0)) {
smallest = left;
} else {
smallest = cur;
}
if ((right < heap.size()) &&
(comp.compare(heap.elementAt(right), heap.elementAt(smallest)) < 0)) {
smallest = right;
}
if (cur != smallest)
{
swap(cur, smallest);
percolateDown(smallest);
}
}
/**
* Moves the element o
at position cur
* as high as it can go in the heap. Returns the new position of the
* element in the heap.
*/
private int percolateUp(int cur, T o)
{
int i = cur;
while ((i > TOP) && (comp.compare(heap.elementAt(parent(i)), o) > 0))
{
T parentElt = heap.elementAt(parent(i));
heap.setElementAt(parentElt, i);
object_indices.put(parentElt, new Integer(i)); // reset index to i (new location)
i = parent(i);
}
// place object in heap at appropriate place
object_indices.put(o, new Integer(i));
heap.setElementAt(o, i);
return i;
}
/**
* Returns the index of the left child of the element at
* index i
of the heap.
* @param i
* @return the index of the left child of the element at
* index i
of the heap
*/
private int lChild(int i)
{
return (i<<1) + 1;
}
/**
* Returns the index of the right child of the element at
* index i
of the heap.
* @param i
* @return the index of the right child of the element at
* index i
of the heap
*/
private int rChild(int i)
{
return (i<<1) + 2;
}
/**
* Returns the index of the parent of the element at
* index i
of the heap.
* @param i
* @return the index of the parent of the element at index i of the heap
*/
private int parent(int i)
{
return (i-1)>>1;
}
/**
* Swaps the positions of the elements at indices i
* and j
of the heap.
* @param i
* @param j
*/
private void swap(int i, int j)
{
T iElt = heap.elementAt(i);
T jElt = heap.elementAt(j);
heap.setElementAt(jElt, i);
object_indices.put(jElt, new Integer(i));
heap.setElementAt(iElt, j);
object_indices.put(iElt, new Integer(j));
}
/**
* Comparator used if none is specified in the constructor.
* @author Joshua O'Madadhain
*/
private class ComparableComparator implements Comparator
{
/**
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@SuppressWarnings("unchecked")
public int compare(T arg0, T arg1)
{
if (!(arg0 instanceof Comparable) || !(arg1 instanceof Comparable))
throw new IllegalArgumentException("Arguments must be Comparable");
return ((Comparable)arg0).compareTo(arg1);
}
}
/**
* Returns an Iterator
that does not support modification
* of the heap.
*/
@Override
public Iterator iterator()
{
return IteratorUtils.unmodifiableIterator(heap.iterator());
}
/**
* This data structure does not support the removal of arbitrary elements.
*/
@Override
public boolean remove(Object o)
{
throw new UnsupportedOperationException();
}
/**
* This data structure does not support the removal of arbitrary elements.
*/
@Override
public boolean removeAll(Collection> c)
{
throw new UnsupportedOperationException();
}
/**
* This data structure does not support the removal of arbitrary elements.
*/
@Override
public boolean retainAll(Collection> c)
{
throw new UnsupportedOperationException();
}
public T element() throws NoSuchElementException
{
T top = this.peek();
if (top == null)
throw new NoSuchElementException();
return top;
}
public boolean offer(T o)
{
return add(o);
}
public T poll()
{
T top = this.peek();
if (top != null)
{
T bottom_elt = heap.lastElement();
heap.setElementAt(bottom_elt, TOP);
object_indices.put(bottom_elt, new Integer(TOP));
heap.setSize(heap.size() - 1); // remove the last element
if (heap.size() > 1)
percolateDown(TOP);
object_indices.remove(top);
}
return top;
}
public T remove()
{
T top = this.poll();
if (top == null)
throw new NoSuchElementException();
return top;
}
}