// PriorityQueue.java
// Java Spatial Index Library
// Copyright (C) 2008 aled@sourceforge.net
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.infomatiq.jsi;
import gnu.trove.TIntArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import gnu.trove.TFloatArrayList;
/**
*
* Priority Queue that stores values as ints and priorities as floats. Uses a
* Heap to sort the priorities; the values are sorted "in step" with the
* priorities.
*
*
* A Heap is simply an array that is kept semi sorted; in particular if the
* elements of the array are arranged in a tree structure; ie
*
*
* 00
* / \
* 01 02
* / \ / \
* 03 04 05 06
* /\ /\ /\ /\
* 07 08 09 10 11 12 13 14
*
*
* then each parent is kept sorted with respect to it's immediate children. E.g.
* 00 < 01, 00 < 02, 02 < 05, 02 < 06
*
*
* This means that the array appears to be sorted, as long as we only ever look
* at element 0.
*
*
* Inserting new elements is much faster than if the entire array was kept
* sorted; a new element is appended to the array, and then recursively swapped
* with each parent to maintain the "parent is sorted w.r.t it's children"
* property.
*
*
* To return the "next" value it is necessary to remove the root element. The
* last element in the array is placed in the root of the tree, and is
* recursively swapped with one of it's children until the "parent is sorted
* w.r.t it's children" property is restored.
*
*
* Random access is slow (eg for deleting a particular value), and is not
* implemented here - if this functionality is required, then a heap probably
* isn't the right data structure.
*
* @author Aled Morris
* @version 1.0b8
*/
public class PriorityQueue {
private static final Logger LOGGER = LoggerFactory.getLogger(PriorityQueue.class);
public static final boolean SORT_ORDER_ASCENDING = true;
public static final boolean SORT_ORDER_DESCENDING = false;
private TIntArrayList values = null;
private TFloatArrayList priorities = null;
private boolean sortOrder = SORT_ORDER_ASCENDING;
private static boolean INTERNAL_CONSISTENCY_CHECKING = false;
public PriorityQueue(boolean sortOrder) {
this(sortOrder, 10);
}
public PriorityQueue(boolean sortOrder, int initialCapacity) {
this.sortOrder = sortOrder;
values = new TIntArrayList(initialCapacity);
priorities = new TFloatArrayList(initialCapacity);
}
/**
* @param p1
* @param p2
* @return true if p1 has an earlier sort order than p2.
*/
private boolean sortsEarlierThan(float p1, float p2) {
if (sortOrder == SORT_ORDER_ASCENDING) {
return p1 < p2;
}
return p2 < p1;
}
// to insert a value, append it to the arrays, then
// reheapify by promoting it to the correct place.
public void insert(int value, float priority) {
values.add(value);
priorities.add(priority);
promote(values.size() - 1, value, priority);
}
private void promote(int index, int value, float priority) {
// Consider the index to be a "hole"; i.e. don't swap priorities/values
// when moving up the tree, simply copy the parent into the hole and
// then consider the parent to be the hole.
// Finally, copy the value/priority into the hole.
while (index > 0) {
int parentIndex = (index - 1) / 2;
float parentPriority = priorities.get(parentIndex);
if (sortsEarlierThan(parentPriority, priority)) {
break;
}
// copy the parent entry into the current index.
values.set(index, values.get(parentIndex));
priorities.set(index, parentPriority);
index = parentIndex;
}
values.set(index, value);
priorities.set(index, priority);
if (INTERNAL_CONSISTENCY_CHECKING) {
check();
}
}
public int size() {
return values.size();
}
public void clear() {
values.clear();
priorities.clear();
}
public void reset() {
values.reset();
priorities.reset();
}
public int getValue() {
return values.get(0);
}
public float getPriority() {
return priorities.get(0);
}
private void demote(int index, int value, float priority) {
int childIndex = (index * 2) + 1; // left child
while (childIndex < values.size()) {
float childPriority = priorities.get(childIndex);
if (childIndex + 1 < values.size()) {
float rightPriority = priorities.get(childIndex + 1);
if (sortsEarlierThan(rightPriority, childPriority)) {
childPriority = rightPriority;
childIndex++; // right child
}
}
if (sortsEarlierThan(childPriority, priority)) {
priorities.set(index, childPriority);
values.set(index, values.get(childIndex));
index = childIndex;
childIndex = (index * 2) + 1;
} else {
break;
}
}
values.set(index, value);
priorities.set(index, priority);
}
// get the value with the lowest priority
// creates a "hole" at the root of the tree.
// The algorithm swaps the hole with the appropriate child, until
// the last entry will fit correctly into the hole (ie is lower
// priority than its children)
public int pop() {
int ret = values.get(0);
// record the value/priority of the last entry
int lastIndex = values.size() - 1;
int tempValue = values.get(lastIndex);
float tempPriority = priorities.get(lastIndex);
values.remove(lastIndex);
priorities.remove(lastIndex);
if (lastIndex > 0) {
demote(0, tempValue, tempPriority);
}
if (INTERNAL_CONSISTENCY_CHECKING) {
check();
}
return ret;
}
public void setSortOrder(boolean sortOrder) {
if (this.sortOrder != sortOrder) {
this.sortOrder = sortOrder;
// reheapify the arrays
for (int i = (values.size() / 2) - 1; i >= 0; i--) {
demote(i, values.get(i), priorities.get(i));
}
}
if (INTERNAL_CONSISTENCY_CHECKING) {
check();
}
}
private void check() {
// for each entry, check that the child entries have a lower or equal
// priority
int lastIndex = values.size() - 1;
for (int i = 0; i < values.size() / 2; i++) {
float currentPriority = priorities.get(i);
int leftIndex = (i * 2) + 1;
if (leftIndex <= lastIndex) {
float leftPriority = priorities.get(leftIndex);
if (sortsEarlierThan(leftPriority, currentPriority)) {
LOGGER.error("Internal error in PriorityQueue");
}
}
int rightIndex = (i * 2) + 2;
if (rightIndex <= lastIndex) {
float rightPriority = priorities.get(rightIndex);
if (sortsEarlierThan(rightPriority, currentPriority)) {
LOGGER.error("Internal error in PriorityQueue");
}
}
}
}
}