1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
|
/*
* File: queue.h
* -------------
* This file exports the <code>Queue</code> 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<ValueType>
* -----------------------
* This class models a linear structure called a <b><i>queue</i></b>
* 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 <typename ValueType>
class Queue {
public:
/*
* Constructor: Queue
* Usage: Queue<ValueType> 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 <code>true</code> 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 <code>value</code> 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 <code>front</code>, 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 <code>true</code> if <code>queue1</code> and <code>queue2</code>
* contain the same elements.
*/
bool operator==(const Queue & queue2) const;
/*
* Operator: !=
* Usage: queue1 != queue2
* -------------------
* Returns <code>true</code> if <code>queue1</code> and <code>queue2</code>
* 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<ValueType> 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 <typename ValueType>
Queue<ValueType>::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 <typename ValueType>
Queue<ValueType>::~Queue() {
/* Empty */
}
template <typename ValueType>
int Queue<ValueType>::size() const {
return count;
}
template <typename ValueType>
bool Queue<ValueType>::isEmpty() const {
return count == 0;
}
template <typename ValueType>
void Queue<ValueType>::clear() {
capacity = INITIAL_CAPACITY;
ringBuffer = Vector<ValueType>(capacity);
head = 0;
tail = 0;
count = 0;
}
template <typename ValueType>
void Queue<ValueType>::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 <typename ValueType>
ValueType Queue<ValueType>::dequeue() {
if (count == 0) error("dequeue: Attempting to dequeue an empty queue");
ValueType result = ringBuffer[head];
head = (head + 1) % capacity;
count--;
return result;
}
template <typename ValueType>
ValueType Queue<ValueType>::peek() const {
if (count == 0) error("peek: Attempting to peek at an empty queue");
return ringBuffer.get(head);
}
template <typename ValueType>
ValueType & Queue<ValueType>::front() {
if (count == 0) error("front: Attempting to read front of an empty queue");
return ringBuffer[head];
}
template <typename ValueType>
ValueType & Queue<ValueType>::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 <typename ValueType>
void Queue<ValueType>::expandRingBufferCapacity() {
Vector<ValueType> copy = ringBuffer;
ringBuffer = Vector<ValueType>(2 * capacity);
for (int i = 0; i < count; i++) {
ringBuffer[i] = copy[(head + i) % capacity];
}
head = 0;
tail = count;
capacity *= 2;
}
template <typename ValueType>
std::string Queue<ValueType>::toString() {
ostringstream os;
os << *this;
return os.str();
}
template <typename ValueType>
bool Queue<ValueType>::operator==(const Queue & queue2) const {
if (size() != queue2.size()) return false;
Queue<ValueType> copy1 = *this;
Queue<ValueType> copy2 = queue2;
for (int i = 0; i < size(); i++) {
if (copy1.dequeue() != copy2.dequeue()) {
return false;
}
}
return true;
}
template <typename ValueType>
bool Queue<ValueType>::operator!=(const Queue & queue2) const {
return !(*this == queue2);
}
template <typename ValueType>
std::ostream & operator<<(std::ostream & os, const Queue<ValueType> & queue) {
os << "{";
Queue<ValueType> 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 <typename ValueType>
std::istream & operator>>(std::istream & is, Queue<ValueType> & 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<std::string>& q);
int hashCode(const Queue<int>& q);
int hashCode(const Queue<char>& q);
int hashCode(const Queue<long>& q);
int hashCode(const Queue<double>& q);
#endif
|