-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathqueue.js
More file actions
368 lines (335 loc) · 10.2 KB
/
queue.js
File metadata and controls
368 lines (335 loc) · 10.2 KB
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
"use strict";
// --------------------Queues--------------------
// A Queue is a data structure that keeps track of items and allows them to be
// processed (i.e. removed from the queue) in the order they have been added.
// This ordering is known FIFO (First-in-first-out).
//
// More info on JavaScript arrays:
// https://en.wikipedia.org/wiki/Queue_(abstract_data_type)
//
// Stacks, on the other hand, track items in REVERSE order. Stack ordering is
// also known as LIFO (Last-in-first-out).
//
// JavaScript arrays are good (i.e. fast) for stacks. push() and pop()
// remove items from the end of an array.
//
// ex.
// var array = [];
// array.push(1);
// array.push(2);
// array.push(3);
// array.pop(); // -> 3, note reverse order
// array.pop(); // -> 2
// array.pop(); // -> 1
//
// We can remove items from the beginning of JavaScript arrays with the
// .unshift() function but this function has to shift all the other items in
// the array back by one space. So if the array is big, this operation
// can take a long time.
//
// We can build fast Queues using a linked lists.
//
// In a linked list, each Item in the Queue has a pointer (i.e. a link or a
// reference) to the next Item, called the 'next' pointer. The Queue itself has
// a pointer to the first and the last Item. We call the pointer to the first
// Item the 'head' pointer the pointer to the last Item the 'tail' pointer.
//
// To add an value to the end of a Queue we:
// - create new Item with value set to new value
// - set the next pointer of the last Item to new Item
// - set the tail pointer to the new Item
//
// To remove an item from the beginning of a queue we:
// - Move the head pointer of the Queue to the next Item
//
// The remove operation is always as fast no matter how big tha Queue gets
// because it only takes one step.
window.Queue = function() {
// The queue starts out empty so this is null.
// When the queue is not empty, this should point to the first <Item> in the
// Queue.
this.head = null;
// The queue starts out empty so this is null.
// When the queue is not empty, this should point to the last <Item> in the
// Queue.
this.tail = null;
// This number should go up and down as you add and pop items from the queue
this.size = 0;
}
// A linked list is made up of items that have pointers (aka links or references)
// to the next item.
// The pointer to the next item is called 'next';
window.Item = function(value, next) {
// The value of the current item
this.value = value;
// The next item (which should also be a Queue.Item)
// If this is the last item in the list, next will be null
this.next = next;
}
// Example: Queue.isEmpty()
// Write a function that returns true if there are no items stored in queue.
//
// ex. new Queue().isEmpty() -> true
Queue.prototype.isEmpty = function() {
return this.size === 0;
}
// Exercise: Queue.getSize()
// Write a function that returns the number of items stored in the queue.
//
// ex. new Queue().getSize() -> 0
Queue.prototype.getSize = function() {
return this.size;
}
// Exercise: Queue.push(value)
// Write function that takes a value and adds that value to the end (i.e.
// tail) of the queue.
Queue.prototype.push = function(value) {
var toAdd = new Item(value);
if (this.size === 0) {
this.head = toAdd;
} else if (this.size === 1){
this.tail = toAdd;
this.head.next = this.tail;
} else {
this.tail.next = toAdd;
this.tail = toAdd;
}
this.size++;
}
// Exercise: Queue.pop()
// Write function that removes the item at the beginning (i.e. head) of the
// queue and returns that item.
//
// This function should throw an exception (i.e. generate an error) if the
// queue is empty.
//
// ex. new Queue().pop() -> Error
Queue.prototype.pop = function() {
if (this.head['value'] === null) {
throw "This queue has no items to be popped";
}
var save = this.head['value'];
if (this.head.next !== null) {
this.head = this.head.next;
this.size--;
} else {
this.head = null;
this.size--;
}
return save;
}
// Exercise: Queue.contains()
// Write a function that takes a value and returns true if that value is stored
// in the queue, false otherwise.
//
// ex. new Queue().contains('something') -> false, queue is empty
Queue.prototype.contains = function(value) {
var i = 0;
var pointer = this.head;
//console.log(this.tail['value']);
while (i < this.size) {
if (pointer['value'] === value) {
return true;
}
pointer = pointer.next;
i++;
}
return false;
}
// Exercise: Queue.peek()
// Write a function that returns the first item of the queue, return null if
// queue is empty.
//
// ex. new Queue().peek() -> null
Queue.prototype.peek = function() {
if (this.size === 0) {
return null;
}
return this.head;
}
// Bonus exercise: Queue.forEach(fun)
// Write a function that takes function 'fun' and calls fun with each item in
// the Queue starting from the first item (i.e. head).
Queue.prototype.forEach = function(fun) {
var i = 0;
var current = this.head;
while (i < this.size) {
fun(current);
current = pointer.next;
i++;
}
}
// --------------------TESTS--------------------
// We've implemented one test suite for you that covers end-to-end
// functionality at the bottom.
// You're responsible for writing the test cases for each function().
describe("Queue.prototype.isEmpty", function() {
it("Queue with items 1, 2, 3 ==> false", function() {
var q = new Queue();
q.push(1);
q.push(2);
q.push(3);
expect(q.isEmpty()).toBe(false);
});
it("New queue with no items ==> true", function() {
var q = new Queue();
expect(q.isEmpty()).toBe(true);
});
});
describe("Queue.prototype.getSize", function() {
it("Queue with items 1, 2, 3 ==> 3", function() {
var q = new Queue();
q.push(1);
q.push(2);
q.push(3);
expect(q.getSize()).toBe(3);
});
it("New queue with no items ==> 0", function() {
var q = new Queue();
expect(q.getSize()).toBe(0);
});
});
describe("Queue.prototype.push", function() {
it("Queue with items 1, 2, 3 ==> 3", function() {
var q = new Queue();
q.push(1);
q.push(2);
q.push(3);
expect(q.getSize()).toBe(3);
});
it("New queue with no items ==> 0", function() {
var q = new Queue();
expect(q.getSize()).toBe(0);
});
});
describe("Queue.prototype.pop", function() {
it("Queue with items 1, 2, 3 then pop, head should be ==> 2", function() {
var q = new Queue();
q.push(1);
q.push(2);
q.push(3);
q.pop();
expect(q.head['value']).toBe(2);
});
it("Queue with items 1, 2, 3, 4, 5 and two pop() calls ==> head should be 3", function() {
var q = new Queue();
q.push(1);
q.push(2);
q.push(3);
q.push(4);
q.push(5);
q.pop();
q.pop();
expect(q.head['value']).toBe(3);
expect(q.getSize()).toBe(3);
});
it("New queue with no items ==> 0", function() {
var q = new Queue();
expect(q.getSize()).toBe(0);
});
});
describe("Queue.prototype.contains", function() {
it("Queue with items 1, 2, 3, 4, 5 .contains(4) ==> true", function() {
var q = new Queue();
q.push(1);
q.push(2);
q.push(3);
q.push(4);
q.push(5);
expect(q.contains(4)).toBe(true);
});
it("Queue with items 1, 2, 3, 4, 5 .contains(7) ==> false", function() {
var q = new Queue();
q.push(1);
q.push(2);
q.push(3);
q.push(4);
q.push(5);
expect(q.contains(7)).toBe(false);
});
it("New queue with no items ==> 0", function() {
var q = new Queue();
expect(q.getSize()).toBe(0);
});
});
describe("Queue.prototype.peek", function() {
// YOUR CODE HERE
});
// This one's a bonus
describe("Queue.prototype.forEach", function() {
// YOUR CODE HERE
});
describe("Queue end-to-end", function() {
it("Push 100 items to queue, then pop them back out, items should come out in same order", function() {
var q = new Queue();
// Generate 100 random numbers
var input = _.range(100).map(_.partial(_.random, 10000));
// Push numbers to queue
_.forEach(input, function(item) {
q.push(item);
});
// Pop all numbers from queue
var out = [];
while (! q.isEmpty()) {
out.push(q.pop());
}
// Items should come out in the order pushed
expect(input).toEqual(out);
});
it("Push and pop the same 2 items 10 times, check that values are updated properly", function() {
var q = new Queue();
_.forEach(_.range(10), function() {
// Should start out empty
expect(q.getSize()).toBe(0);
expect(q.isEmpty()).toBe(true);
expect(q.contains('x')).toBe(false);
expect(q.contains('y')).toBe(false);
// Add x, check contents, check size
q.push('x');
expect(q.contains('x')).toBe(true);
expect(q.contains('y')).toBe(false);
expect(q.getSize()).toBe(1);
// Add x, check contents, check size
q.push('y');
expect(q.contains('x')).toBe(true);
expect(q.contains('y')).toBe(true);
expect(q.getSize()).toBe(2);
// Pop x, check contents, check size
expect(q.pop()).toBe('x');
expect(q.contains('x')).toBe(false);
expect(q.contains('y')).toBe(true);
expect(q.getSize()).toBe(1);
// Pop y, check empty
expect(q.pop()).toBe('y');
expect(q.contains('x')).toBe(false);
expect(q.contains('y')).toBe(false);
expect(q.getSize()).toBe(0);
expect(q.isEmpty()).toBe(true);
})
});
it("Bonus: queue should be faster than an array for adding to the beginning", function() {
// Run a function multiple times and measure time taken to run
function time(fun) {
var start = Date.now();
_.range(1000).map(fun);
return Date.now() - start;
}
function array() {
var array = [];
_.range(1000).forEach(function(item) {
array.unshift(item);
});
}
function queue() {
var q = new Queue();
_.range(1000).forEach(function(item) {
q.push(item);
});
}
var arrayTime = time(array);
var queueTime = time(queue);
// Array time should be greater, meaning it took longer, i.e. it was slower
console.log('Queue time: %s Array time: %s', queueTime, arrayTime);
expect(arrayTime > queueTime).toBe(true);
});
});