/*
* File: pqueue.h
* --------------
* This file exports the PriorityQueue class, a
* collection in which values are processed in priority order.
*/
#ifndef _pqueue_h
#define _pqueue_h
#include "vector.h"
/*
* Class: PriorityQueue
* -------------------------------
* This class models a structure called a priority queue
* in which values are processed in order of priority. As in conventional
* English usage, lower priority numbers correspond to higher effective
* priorities, so that a priority 1 item takes precedence over a
* priority 2 item.
*/
template
class PriorityQueue {
public:
/*
* Constructor: PriorityQueue
* Usage: PriorityQueue pq;
* -----------------------------------
* Initializes a new priority queue, which is initially empty.
*/
PriorityQueue();
/*
* Destructor: ~PriorityQueue
* --------------------------
* Frees any heap storage associated with this priority queue.
*/
virtual ~PriorityQueue();
/*
* Method: size
* Usage: int n = pq.size();
* -------------------------
* Returns the number of values in the priority queue.
*/
int size() const;
/*
* Method: isEmpty
* Usage: if (pq.isEmpty()) ...
* ----------------------------
* Returns true if the priority queue contains no elements.
*/
bool isEmpty() const;
/*
* Method: clear
* Usage: pq.clear();
* ------------------
* Removes all elements from the priority queue.
*/
void clear();
/*
* Method: enqueue
* Usage: pq.enqueue(value, priority);
* -----------------------------------
* Adds value to the queue with the specified priority.
* Lower priority numbers correspond to higher priorities, which
* means that all priority 1 elements are dequeued before any
* priority 2 elements.
*/
void enqueue(ValueType value, double priority);
/*
* Method: changePriority
* Usage: pq.changePriority(value, newPriority);
* -----------------------------------
* Adjusts value in the queue to now have the specified new priority,
* which must be at least as urgent (lower number) than that value's previous
* priority in the queue.
* Throws an error if the element value is not present in the queue, or if the
* new priority passed is not at least as urgent as its current priority.
*/
void changePriority(ValueType value, double newPriority);
/*
* Method: dequeue
* Usage: ValueType first = pq.dequeue();
* --------------------------------------
* Removes and returns the highest priority value. If multiple
* entries in the queue have the same priority, those values are
* dequeued in the same order in which they were enqueued.
*/
ValueType dequeue();
/*
* Method: peek
* Usage: ValueType first = pq.peek();
* -----------------------------------
* Returns the value of highest priority in the queue, without
* removing it.
*/
ValueType peek() const;
/*
* Method: peekPriority
* Usage: double priority = pq.peekPriority();
* -------------------------------------------
* Returns the priority of the first element in the queue, without
* removing it.
*/
double peekPriority() const;
/*
* Method: front
* Usage: ValueType first = pq.front();
* ------------------------------------
* Returns the first value in the queue by reference.
*/
ValueType & front();
/*
* Method: back
* Usage: ValueType last = pq.back();
* ----------------------------------
* Returns the last value in the queue by reference.
*/
ValueType & back();
/*
* Method: toString
* Usage: string str = pq.toString();
* ----------------------------------
* Converts the queue to a printable string representation.
*/
std::string toString();
/* Private section */
/**********************************************************************/
/* Note: Everything below this point in the file is logically part */
/* of the implementation and should not be of interest to clients. */
/**********************************************************************/
/*
* Implementation notes: PriorityQueue data structure
* --------------------------------------------------
* The PriorityQueue class is implemented using a data structure called
* a heap.
*/
private:
/* Type used for each heap entry */
struct HeapEntry {
ValueType value;
double priority;
long sequence;
};
/* Instance variables */
Vector heap;
long enqueueCount;
int backIndex;
int count;
int capacity;
/* Private function prototypes */
void enqueueHeap(ValueType & value, double priority);
ValueType dequeueHeap();
bool takesPriority(int i1, int i2);
void swapHeapEntries(int i1, int i2);
};
extern void error(std::string msg);
template
PriorityQueue::PriorityQueue() {
clear();
}
/*
* Implementation notes: ~PriorityQueue destructor
* -----------------------------------------------
* All of the dynamic memory is allocated in the Vector class,
* so no work is required at this level.
*/
template
PriorityQueue::~PriorityQueue() {
/* Empty */
}
template
int PriorityQueue::size() const {
return count;
}
template
bool PriorityQueue::isEmpty() const {
return count == 0;
}
template
void PriorityQueue::clear() {
heap.clear();
count = 0;
}
template
void PriorityQueue::enqueue(ValueType value, double priority) {
if (count == heap.size()) heap.add(HeapEntry());
int index = count++;
heap[index].value = value;
heap[index].priority = priority;
heap[index].sequence = enqueueCount++;
if (index == 0 || takesPriority(backIndex, index)) backIndex = index;
while (index > 0) {
int parent = (index - 1) / 2;
if (takesPriority(parent, index)) break;
swapHeapEntries(parent, index);
index = parent;
}
}
template
void PriorityQueue::changePriority(ValueType value, double newPriority) {
// parts of this implementation are adapted from TrailblazerPQueue.h,
// which was written by Keith Schwarz
if (!(newPriority == newPriority)) {
error("PriorityQueue::changePriority(" + genericValueToString(value) + ", "
+ realToString(newPriority) + "): Attempted to use NaN as a priority.");
}
// find the element in the pqueue; must use a simple iteration over elements
for (int i = 0; i < count; i++) {
if (heap[i].value == value) {
if (heap[i].priority < newPriority) {
error("PriorityQueue::changePriority(" + genericValueToString(value) + ", "
+ realToString(newPriority)
+ "): new priority cannot be less urgent than current priority of "
+ realToString(heap[i].priority) + ".");
}
heap[i].priority = newPriority;
// after changing the priority, must percolate up to proper level
// to maintain heap ordering
while (i > 0) {
int parent = (i - 1) / 2;
if (takesPriority(parent, i)) break;
swapHeapEntries(parent, i);
i = parent;
}
return;
}
}
// if we get here, the element was not ever found
error("PriorityQueue::changePriority(" + genericValueToString(value) + ", "
+ realToString(newPriority) + "): Element value not found.");
}
/*
* Implementation notes: dequeue, peek, peekPriority
* -------------------------------------------------
* These methods must check for an empty queue and report an error
* if there is no first element.
*/
template
ValueType PriorityQueue::dequeue() {
if (count == 0) error("dequeue: Attempting to dequeue an empty queue");
count--;
bool wasBack = (backIndex == count);
ValueType value = heap[0].value;
swapHeapEntries(0, count);
int index = 0;
while (true) {
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left >= count) break;
int child = left;
if (right < count && takesPriority(right, left)) child = right;
if (takesPriority(index, child)) break;
swapHeapEntries(index, child);
index = child;
}
if (wasBack) backIndex = index;
return value;
}
template
ValueType PriorityQueue::peek() const {
if (count == 0) error("peek: Attempting to peek at an empty queue");
return heap.get(0).value;
}
template
double PriorityQueue::peekPriority() const {
if (count == 0) error("peekPriority: Attempting to peek at an empty queue");
return heap.get(0).priority;
}
template
ValueType & PriorityQueue::front() {
if (count == 0) error("front: Attempting to read front of an empty queue");
return heap.get(0).value;
}
template
ValueType & PriorityQueue::back() {
if (count == 0) error("back: Attempting to read back of an empty queue");
return heap.get(backIndex).value;
}
template
bool PriorityQueue::takesPriority(int i1, int i2) {
if (heap[i1].priority < heap[i2].priority) return true;
if (heap[i1].priority > heap[i2].priority) return false;
return (heap[i1].sequence < heap[i2].sequence);
}
template
void PriorityQueue::swapHeapEntries(int i1, int i2) {
HeapEntry entry = heap[i1];
heap[i1] = heap[i2];
heap[i2] = entry;
}
template
std::string PriorityQueue::toString() {
ostringstream os;
os << *this;
return os.str();
}
template
std::ostream & operator<<(std::ostream & os,
const PriorityQueue & pq) {
os << "{";
PriorityQueue copy = pq;
int len = pq.size();
for (int i = 0; i < len; i++) {
if (i > 0) os << ", ";
os << copy.peekPriority() << ":";
writeGenericValue(os, copy.dequeue(), true);
}
return os << "}";
}
template
std::istream & operator>>(std::istream & is, PriorityQueue & pq) {
char ch;
is >> ch;
if (ch != '{') error("operator >>: Missing {");
pq.clear();
is >> ch;
if (ch != '}') {
is.unget();
while (true) {
double priority;
is >> priority >> ch;
if (ch != ':') error("operator >>: Missing colon after priority");
ValueType value;
readGenericValue(is, value);
pq.enqueue(value, priority);
is >> ch;
if (ch == '}') break;
if (ch != ',') {
error(std::string("operator >>: Unexpected character ") + ch);
}
}
}
return is;
}
#endif