/* * File: queue.h * ------------- * This file exports the Queue class, a collection * in which values are ordinarily processed in a first-in/first-out * (FIFO) order. */ #ifndef _queue_h #define _queue_h #include "vector.h" /* * Class: Queue * ----------------------- * This class models a linear structure called a queue * in which values are added at one end and removed from the other. * This discipline gives rise to a first-in/first-out behavior (FIFO) * that is the defining feature of queues. */ template class Queue { public: /* * Constructor: Queue * Usage: Queue queue; * ------------------------------ * Initializes a new empty queue. */ Queue(); /* * Destructor: ~Queue * ------------------ * Frees any heap storage associated with this queue. */ virtual ~Queue(); /* * Method: size * Usage: int n = queue.size(); * ---------------------------- * Returns the number of values in the queue. */ int size() const; /* * Method: isEmpty * Usage: if (queue.isEmpty()) ... * ------------------------------- * Returns true if the queue contains no elements. */ bool isEmpty() const; /* * Method: clear * Usage: queue.clear(); * --------------------- * Removes all elements from the queue. */ void clear(); /* * Method: enqueue * Usage: queue.enqueue(value); * ---------------------------- * Adds value to the end of the queue. */ void enqueue(ValueType value); /* * Method: dequeue * Usage: ValueType first = queue.dequeue(); * ----------------------------------------- * Removes and returns the first item in the queue. */ ValueType dequeue(); /* * Method: peek * Usage: ValueType first = queue.peek(); * -------------------------------------- * Returns the first value in the queue, without removing it. For * compatibility with the STL classes, this method is also exported * under the name front, in which case it returns the * value by reference. */ ValueType peek() const; /* * Method: front * Usage: ValueType first = queue.front(); * --------------------------------------- * Returns the first value in the queue by reference. */ ValueType & front(); /* * Method: back * Usage: ValueType last = queue.back(); * ------------------------------------- * Returns the last value in the queue by reference. */ ValueType & back(); /* * Method: toString * Usage: string str = queue.toString(); * ------------------------------------- * Converts the queue to a printable string representation. */ std::string toString(); /* * Operator: == * Usage: queue1 == queue2 * ------------------- * Returns true if queue1 and queue2 * contain the same elements. */ bool operator==(const Queue & queue2) const; /* * Operator: != * Usage: queue1 != queue2 * ------------------- * Returns true if queue1 and queue2 * do not contain the same elements. */ bool operator!=(const Queue & queue2) const; /* 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: Queue data structure * ------------------------------------------ * The Queue class is implemented using a ring buffer. */ private: /* Instance variables */ Vector ringBuffer; int count; int capacity; int head; int tail; /* Private functions */ void expandRingBufferCapacity(); }; extern void error(std::string msg); /* * Implementation notes: Queue data structure * ------------------------------------------ * The array-based queue stores the elements in successive index * positions in a vector, just as a stack does. What makes the * queue structure more complex is the need to avoid shifting * elements as the queue expands and contracts. In the array * model, this goal is achieved by keeping track of both the * head and tail indices. The tail index increases by one each * time an element is enqueued, and the head index increases by * one each time an element is dequeued. Each index therefore * marches toward the end of the allocated vector and will * eventually reach the end. Rather than allocate new memory, * this implementation lets each index wrap around back to the * beginning as if the ends of the array of elements were joined * to form a circle. This representation is called a ring buffer. */ const int INITIAL_CAPACITY = 10; /* * Implementation notes: Queue constructor * --------------------------------------- * The constructor must allocate the array storage for the queue * elements and initialize the fields of the object. */ template Queue::Queue() { clear(); } /* * Implementation notes: ~Queue destructor * --------------------------------------- * All of the dynamic memory is allocated in the Vector class, * so no work is required at this level. */ template Queue::~Queue() { /* Empty */ } template int Queue::size() const { return count; } template bool Queue::isEmpty() const { return count == 0; } template void Queue::clear() { capacity = INITIAL_CAPACITY; ringBuffer = Vector(capacity); head = 0; tail = 0; count = 0; } template void Queue::enqueue(ValueType value) { if (count >= capacity - 1) expandRingBufferCapacity(); ringBuffer[tail] = value; tail = (tail + 1) % capacity; count++; } /* * Implementation notes: dequeue, peek * ----------------------------------- * These methods must check for an empty queue and report an error * if there is no first element. */ template ValueType Queue::dequeue() { if (count == 0) error("dequeue: Attempting to dequeue an empty queue"); ValueType result = ringBuffer[head]; head = (head + 1) % capacity; count--; return result; } template ValueType Queue::peek() const { if (count == 0) error("peek: Attempting to peek at an empty queue"); return ringBuffer.get(head); } template ValueType & Queue::front() { if (count == 0) error("front: Attempting to read front of an empty queue"); return ringBuffer[head]; } template ValueType & Queue::back() { if (count == 0) error("back: Attempting to read back of an empty queue"); return ringBuffer[(tail + capacity - 1) % capacity]; } /* * Implementation notes: expandRingBufferCapacity * ---------------------------------------------- * This private method doubles the capacity of the ringBuffer vector. * Note that this implementation also shifts all the elements back to * the beginning of the vector. */ template void Queue::expandRingBufferCapacity() { Vector copy = ringBuffer; ringBuffer = Vector(2 * capacity); for (int i = 0; i < count; i++) { ringBuffer[i] = copy[(head + i) % capacity]; } head = 0; tail = count; capacity *= 2; } template std::string Queue::toString() { ostringstream os; os << *this; return os.str(); } template bool Queue::operator==(const Queue & queue2) const { if (size() != queue2.size()) return false; Queue copy1 = *this; Queue copy2 = queue2; for (int i = 0; i < size(); i++) { if (copy1.dequeue() != copy2.dequeue()) { return false; } } return true; } template bool Queue::operator!=(const Queue & queue2) const { return !(*this == queue2); } template std::ostream & operator<<(std::ostream & os, const Queue & queue) { os << "{"; Queue copy = queue; int len = queue.size(); for (int i = 0; i < len; i++) { if (i > 0) os << ", "; writeGenericValue(os, copy.dequeue(), true); } return os << "}"; } template std::istream & operator>>(std::istream & is, Queue & queue) { char ch; is >> ch; if (ch != '{') error("operator >>: Missing {"); queue.clear(); is >> ch; if (ch != '}') { is.unget(); while (true) { ValueType value; readGenericValue(is, value); queue.enqueue(value); is >> ch; if (ch == '}') break; if (ch != ',') { error(std::string("operator >>: Unexpected character ") + ch); } } } return is; } // hashing functions for queues; defined in hashmap.cpp int hashCode(const Queue& q); int hashCode(const Queue& q); int hashCode(const Queue& q); int hashCode(const Queue& q); int hashCode(const Queue& q); #endif