/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.collect;
/***********************************************************************
* File: FibonacciHeap.java Author: Keith Schwarz (htiek@cs.stanford.edu)
*
* An implementation of a priority queue backed by a Fibonacci heap, as
* described by Fredman and Tarjan. Fibonacci heaps are interesting
* theoretically because they have asymptotically good runtime guarantees for
* many operations. In particular, insert, peek, and decrease-key all run in
* amortized O(1) time. dequeueMin and delete each run in amortized O(lg n)
* time. This allows algorithms that rely heavily on decrease-key to gain
* significant performance boosts. For example, Dijkstra's algorithm for
* single-source shortest paths can be shown to run in O(m + n lg n) using a
* Fibonacci heap, compared to O(m lg n) using a standard binary or binomial
* heap.
*
* Internally, a Fibonacci heap is represented as a circular, doubly-linked list
* of trees obeying the min-heap property. Each node stores pointers to its
* parent (if any) and some arbitrary child. Additionally, every node stores its
* degree (the number of children it has) and whether it is a "marked" node.
* Finally, each Fibonacci heap stores a pointer to the tree with the minimum
* value.
*
* To insert a node into a Fibonacci heap, a singleton tree is created and
* merged into the rest of the trees. The merge operation works by simply
* splicing together the doubly-linked lists of the two trees, then updating the
* min pointer to be the smaller of the minima of the two heaps. Peeking at the
* smallest element can therefore be accomplished by just looking at the min
* element. All of these operations complete in O(1) time.
*
* The tricky operations are dequeueMin and decreaseKey. dequeueMin works by
* removing the root of the tree containing the smallest element, then merging
* its children with the topmost roots. Then, the roots are scanned and merged
* so that there is only one tree of each degree in the root list. This works by
* maintaining a dynamic array of trees, each initially null, pointing to the
* roots of trees of each dimension. The list is then scanned and this array is
* populated. Whenever a conflict is discovered, the appropriate trees are
* merged together until no more conflicts exist. The resulting trees are then
* put into the root list. A clever analysis using the potential method can be
* used to show that the amortized cost of this operation is O(lg n), see
* "Introduction to Algorithms, Second Edition" by Cormen, Rivest, Leiserson,
* and Stein for more details.
*
* The other hard operation is decreaseKey, which works as follows. First, we
* update the key of the node to be the new value. If this leaves the node
* smaller than its parent, we're done. Otherwise, we cut the node from its
* parent, add it as a root, and then mark its parent. If the parent was already
* marked, we cut that node as well, recursively mark its parent, and continue
* this process. This can be shown to run in O(1) amortized time using yet
* another clever potential function. Finally, given this function, we can
* implement delete by decreasing a key to -\infty, then calling dequeueMin to
* extract it.
*/
// For ArrayList
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* A class representing a Fibonacci heap.
*
* @param T
* The type of elements to store in the heap.
* @author Keith Schwarz (htiek@cs.stanford.edu)
*/
public final class FibonacciHeap<K extends Comparable<K>, T> {
/*
* In order for all of the Fibonacci heap operations to complete in O(1),
* clients need to have O(1) access to any element in the heap. We make this
* work by having each insertion operation produce a handle to the node in
* the tree. In actuality, this handle is the node itself, but we guard
* against external modification by marking the internal fields private.
*/
public static final class Entry<K extends Comparable<K>, T> {
private int mDegree = 0; // Number of children
private boolean mIsMarked = false; // Whether this node is marked
private Entry<K, T> mNext; // Next and previous elements in the list
private Entry<K, T> mPrev;
private Entry<K, T> mParent; // Parent in the tree, if any.
private Entry<K, T> mChild; // Child node, if any.
private T mElem; // Element being stored here
private K mPriority; // Its priority
/**
* Constructs a new Entry that holds the given element with the
* indicated priority.
*
* @param elem
* The element stored in this node.
* @param priority
* The priority of this element.
*/
private Entry(T elem, K priority) {
mNext = mPrev = this;
mElem = elem;
mPriority = priority;
}
/**
* Returns the priority of this element.
*
* @return The priority of this element.
*/
public K getPriority() {
return mPriority;
}
/**
* Returns the element represented by this heap entry.
*
* @return The element represented by this heap entry.
*/
public T getValue() {
return mElem;
}
/**
* Sets the element associated with this heap entry.
*
* @param value
* The element to associate with this heap entry.
*/
public void setValue(T value) {
mElem = value;
}
}
/**
* Given two Fibonacci heaps, returns a new Fibonacci heap that contains all
* of the elements of the two heaps. Each of the input heaps is
* destructively modified by having all its elements removed. You can
* continue to use those heaps, but be aware that they will be empty after
* this call completes.
*
* @param one
* The first Fibonacci heap to merge.
* @param two
* The second Fibonacci heap to merge.
* @return A new FibonacciHeap containing all of the elements of both heaps.
*/
public static <K extends Comparable<K>, T> FibonacciHeap<K, T> merge(FibonacciHeap<K, T> one,
FibonacciHeap<K, T> two) {
/* Create a new FibonacciHeap to hold the result. */
FibonacciHeap<K, T> result = new FibonacciHeap<K, T>(one.minPriority);
/*
* Merge the two Fibonacci heap root lists together. This helper
* function also computes the min of the two lists, so we can store the
* result in the mMin field of the new heap.
*/
result.mMin = mergeLists(one.mMin, two.mMin);
/*
* The size of the new heap is the sum of the sizes of the input heaps.
*/
result.mSize = one.mSize + two.mSize;
/* Clear the old heaps. */
one.mSize = two.mSize = 0;
one.mMin = null;
two.mMin = null;
/* Return the newly-merged heap. */
return result;
}
/**
* Utility function which, given two pointers into disjoint circularly-
* linked lists, merges the two lists together into one circularly-linked
* list in O(1) time. Because the lists may be empty, the return value is
* the only pointer that's guaranteed to be to an element of the resulting
* list.
*
* This function assumes that one and two are the minimum elements of the
* lists they are in, and returns a pointer to whichever is smaller. If this
* condition does not hold, the return value is some arbitrary pointer into
* the doubly-linked list.
*
* @param one
* A pointer into one of the two linked lists.
* @param two
* A pointer into the other of the two linked lists.
* @return A pointer to the smallest element of the resulting list.
*/
private static <K extends Comparable<K>, T> Entry<K, T> mergeLists(Entry<K, T> one, Entry<K, T> two) {
/*
* There are four cases depending on whether the lists are null or not.
* We consider each separately.
*/
if (one == null && two == null) { // Both null, resulting list is null.
return null;
} else if (one != null && two == null) { // Two is null, result is one.
return one;
} else if (one == null && two != null) { // One is null, result is two.
return two;
} else { // Both non-null; actually do the splice.
/*
* This is actually not as easy as it seems. The idea is that we'll
* have two lists that look like this:
*
* +----+ +----+ +----+ | |--N->|one |--N->| | | |<-P--| |<-P--| |
* +----+ +----+ +----+
*
*
* +----+ +----+ +----+ | |--N->|two |--N->| | | |<-P--| |<-P--| |
* +----+ +----+ +----+
*
* And we want to relink everything to get
*
* +----+ +----+ +----+---+ | |--N->|one | | | | | |<-P--| | | |<+ |
* +----+ +----+<-\ +----+ | | \ P | | N \ N | +----+ +----+
* \->+----+ | | | |--N->|two | | | | | | |<-P--| | | | | P +----+
* +----+ +----+ | | ^ | | | | +-------------+ | +-----------------+
*/
Entry<K, T> oneNext = one.mNext; // Cache this since we're about to
// overwrite it.
one.mNext = two.mNext;
one.mNext.mPrev = one;
two.mNext = oneNext;
two.mNext.mPrev = two;
/* Return a pointer to whichever's smaller. */
return one.mPriority.compareTo(two.mPriority) < 0 ? one : two;
}
}
private final K minPriority;
/* Pointer to the minimum element in the heap. */
private Entry<K, T> mMin = null;
/*
* Cached size of the heap, so we don't have to recompute this explicitly.
*/
private int mSize = 0;
public FibonacciHeap(K minPriority) {
super();
this.minPriority = minPriority;
}
/**
* Utility function which, given a user-specified priority, checks whether
* it's a valid K and throws an IllegalArgumentException otherwise.
*
* @param priority
* The user's specified priority.
* @throws IllegalArgumentException
* If it is not valid.
*/
protected void checkPriority(K priority) {
}
/**
* Cuts a node from its parent. If the parent was already marked,
* recursively cuts that node from its parent as well.
*
* @param entry
* The node to cut from its parent.
*/
private void cutNode(Entry<K, T> entry) {
/* Begin by clearing the node's mark, since we just cut it. */
entry.mIsMarked = false;
/* Base case: If the node has no parent, we're done. */
if (entry.mParent == null) {
return;
}
/* Rewire the node's siblings around it, if it has any siblings. */
if (entry.mNext != entry) { // Has siblings
entry.mNext.mPrev = entry.mPrev;
entry.mPrev.mNext = entry.mNext;
}
/*
* If the node is the one identified by its parent as its child, we need
* to rewrite that pointer to point to some arbitrary other child.
*/
if (entry.mParent.mChild == entry) {
/* If there are any other children, pick one of them arbitrarily. */
if (entry.mNext != entry) {
entry.mParent.mChild = entry.mNext;
}
/*
* Otherwise, there aren't any children left and we should clear the
* pointer and drop the node's degree.
*/
else {
entry.mParent.mChild = null;
}
}
/* Decrease the degree of the parent, since it just lost a child. */
--entry.mParent.mDegree;
/*
* Splice this tree into the root list by converting it to a singleton
* and invoking the merge subroutine.
*/
entry.mPrev = entry.mNext = entry;
mMin = mergeLists(mMin, entry);
/*
* Mark the parent and recursively cut it if it's already been marked.
*/
if (entry.mParent.mIsMarked) {
cutNode(entry.mParent);
} else {
entry.mParent.mIsMarked = true;
}
/* Clear the relocated node's parent; it's now a root. */
entry.mParent = null;
}
/**
* Decreases the key of the specified element to the new priority. If the
* new priority is greater than the old priority, this function throws an
* IllegalArgumentException. The new priority must be a finite K, so you
* cannot set the priority to be NaN, or +/- infinity. Doing so also throws
* an IllegalArgumentException.
*
* It is assumed that the entry belongs in this heap. For efficiency
* reasons, this is not checked at runtime.
*
* @param entry
* The element whose priority should be decreased.
* @param newPriority
* The new priority to associate with this entry.
* @throws IllegalArgumentException
* If the new priority exceeds the old priority, or if the
* argument is not a finite K.
*/
public void decreaseKey(Entry<K, T> entry, K newPriority) {
checkPriority(newPriority);
if (newPriority.compareTo(entry.mPriority) > 0) {
throw new IllegalArgumentException("New priority exceeds old.");
}
/* Forward this to a helper function. */
decreaseKeyUnchecked(entry, newPriority);
}
/**
* Decreases the key of a node in the tree without doing any checking to
* ensure that the new priority is valid.
*
* @param entry
* The node whose key should be decreased.
* @param priority
* The node's new priority.
*/
private void decreaseKeyUnchecked(Entry<K, T> entry, K priority) {
/* First, change the node's priority. */
entry.mPriority = priority;
/*
* If the node no longer has a higher priority than its parent, cut it.
* Note that this also means that if we try to run a delete operation
* that decreases the key to -infinity, it's guaranteed to cut the node
* from its parent.
*/
if (entry.mParent != null && entry.mPriority.compareTo(entry.mParent.mPriority) <= 0) {
cutNode(entry);
}
/*
* If our new value is the new min, mark it as such. Note that if we
* ended up decreasing the key in a way that ties the current minimum
* priority, this will change the min accordingly.
*/
if (entry.mPriority.compareTo(mMin.mPriority) <= 0) {
mMin = entry;
}
}
/**
* Deletes this Entry from the Fibonacci heap that contains it.
*
* It is assumed that the entry belongs in this heap. For efficiency
* reasons, this is not checked at runtime.
*
* @param entry
* The entry to delete.
*/
public void delete(Entry<K, T> entry) {
/*
* Use decreaseKey to drop the entry's key to -infinity. This will
* guarantee that the node is cut and set to the global minimum.
*/
decreaseKeyUnchecked(entry, minPriority);
/* Call dequeueMin to remove it. */
dequeueMin();
}
/**
* Dequeues and returns the minimum element of the Fibonacci heap. If the
* heap is empty, this throws a NoSuchElementException.
*
* @return The smallest element of the Fibonacci heap.
* @throws NoSuchElementException
* If the heap is empty.
*/
public Entry<K, T> dequeueMin() {
/* Check for whether we're empty. */
if (isEmpty()) {
throw new NoSuchElementException("Heap is empty.");
}
/*
* Otherwise, we're about to lose an element, so decrement the number of
* entries in this heap.
*/
--mSize;
/* Grab the minimum element so we know what to return. */
Entry<K, T> minElem = mMin;
/*
* Now, we need to get rid of this element from the list of roots. There
* are two cases to consider. First, if this is the only element in the
* list of roots, we set the list of roots to be null by clearing mMin.
* Otherwise, if it's not null, then we write the elements next to the
* min element around the min element to remove it, then arbitrarily
* reassign the min.
*/
if (mMin.mNext == mMin) { // Case one
mMin = null;
} else { // Case two
mMin.mPrev.mNext = mMin.mNext;
mMin.mNext.mPrev = mMin.mPrev;
mMin = mMin.mNext; // Arbitrary element of the root list.
}
/*
* Next, clear the parent fields of all of the min element's children,
* since they're about to become roots. Because the elements are stored
* in a circular list, the traversal is a bit complex.
*/
if (minElem.mChild != null) {
/* Keep track of the first visited node. */
Entry<K, ?> curr = minElem.mChild;
do {
curr.mParent = null;
/*
* Walk to the next node, then stop if this is the node we
* started at.
*/
curr = curr.mNext;
} while (curr != minElem.mChild);
}
/*
* Next, splice the children of the root node into the topmost list,
* then set mMin to point somewhere in that list.
*/
mMin = mergeLists(mMin, minElem.mChild);
/* If there are no entries left, we're done. */
if (mMin == null) {
return minElem;
}
/*
* Next, we need to coalsce all of the roots so that there is only one
* tree of each degree. To track trees of each size, we allocate an
* ArrayList where the entry at position i is either null or the unique
* tree of degree i.
*/
List<Entry<K, T>> treeTable = new ArrayList<Entry<K, T>>();
/*
* We need to traverse the entire list, but since we're going to be
* messing around with it we have to be careful not to break our
* traversal order mid-stream. One major challenge is how to detect
* whether we're visiting the same node twice. To do this, we'll spent a
* bit of overhead adding all of the nodes to a list, and then will
* visit each element of this list in order.
*/
List<Entry<K, T>> toVisit = new ArrayList<Entry<K, T>>();
/*
* To add everything, we'll iterate across the elements until we find
* the first element twice. We check this by looping while the list is
* empty or while the current element isn't the first element of that
* list.
*/
for (Entry<K, T> curr = mMin; toVisit.isEmpty() || toVisit.get(0) != curr; curr = curr.mNext) {
toVisit.add(curr);
}
/* Traverse this list and perform the appropriate unioning steps. */
for (Entry<K, T> curr : toVisit) {
/* Keep merging until a match arises. */
while (true) {
/*
* Ensure that the list is long enough to hold an element of
* this degree.
*/
while (curr.mDegree >= treeTable.size()) {
treeTable.add(null);
}
/*
* If nothing's here, we're can record that this tree has this
* size and are done processing.
*/
if (treeTable.get(curr.mDegree) == null) {
treeTable.set(curr.mDegree, curr);
break;
}
/* Otherwise, merge with what's there. */
Entry<K, T> other = treeTable.get(curr.mDegree);
treeTable.set(curr.mDegree, null); // Clear the slot
/*
* Determine which of the two trees has the smaller root,
* storing the two tree accordingly.
*/
Entry<K, T> min = other.mPriority.compareTo(curr.mPriority) < 0 ? other : curr;
Entry<K, T> max = other.mPriority.compareTo(curr.mPriority) < 0 ? curr : other;
/*
* Break max out of the root list, then merge it into min's
* child list.
*/
max.mNext.mPrev = max.mPrev;
max.mPrev.mNext = max.mNext;
/* Make it a singleton so that we can merge it. */
max.mNext = max.mPrev = max;
min.mChild = mergeLists(min.mChild, max);
/* Reparent max appropriately. */
max.mParent = min;
/* Clear max's mark, since it can now lose another child. */
max.mIsMarked = false;
/* Increase min's degree; it now has another child. */
++min.mDegree;
/* Continue merging this tree. */
curr = min;
}
/*
* Update the global min based on this node. Note that we compare
* for <= instead of < here. That's because if we just did a
* reparent operation that merged two different trees of equal
* priority, we need to make sure that the min pointer points to the
* root-level one.
*/
if (curr.mPriority.compareTo(mMin.mPriority) <= 0) {
mMin = curr;
}
}
return minElem;
}
/**
* Inserts the specified element into the Fibonacci heap with the specified
* priority. Its priority must be a valid K, so you cannot set the priority
* to NaN.
*
* @param value
* The value to insert.
* @param priority
* Its priority, which must be valid.
* @return An Entry representing that element in the tree.
*/
public Entry<K, T> enqueue(T value, K priority) {
checkPriority(priority);
/*
* Create the entry object, which is a circularly-linked list of length
* one.
*/
Entry<K, T> result = new Entry<K, T>(value, priority);
/* Merge this singleton list with the tree list. */
mMin = mergeLists(mMin, result);
/* Increase the size of the heap; we just added something. */
++mSize;
/* Return the reference to the new element. */
return result;
}
/**
* Returns whether the heap is empty.
*
* @return Whether the heap is empty.
*/
public boolean isEmpty() {
return mMin == null;
}
/**
* Returns an Entry object corresponding to the minimum element of the
* Fibonacci heap, throwing a NoSuchElementException if the heap is empty.
*
* @return The smallest element of the heap.
* @throws NoSuchElementException
* If the heap is empty.
*/
public Entry<K, T> min() {
if (isEmpty()) {
throw new NoSuchElementException("Heap is empty.");
}
return mMin;
}
/**
* Returns the number of elements in the heap.
*
* @return The number of elements in the heap.
*/
public int size() {
return mSize;
}
}