What is the best way to implement a Stack and a Queue in JavaScript?
I'm looking to do the shunting-yard algorithm and I'm going to need these data-structures.
What is the best way to implement a Stack and a Queue in JavaScript?
I'm looking to do the shunting-yard algorithm and I'm going to need these data-structures.
You can use your own customize class based on the concept, here the code snippet which you can use to do the stuff
/*
* Stack implementation in JavaScript
*/
function Stack() {
this.top = null;
this.count = 0;
this.getCount = function() {
return this.count;
}
this.getTop = function() {
return this.top;
}
this.push = function(data) {
var node = {
data: data,
next: null
}
node.next = this.top;
this.top = node;
this.count++;
}
this.peek = function() {
if (this.top === null) {
return null;
} else {
return this.top.data;
}
}
this.pop = function() {
if (this.top === null) {
return null;
} else {
var out = this.top;
this.top = this.top.next;
if (this.count > 0) {
this.count--;
}
return out.data;
}
}
this.displayAll = function() {
if (this.top === null) {
return null;
} else {
var arr = new Array();
var current = this.top;
//console.log(current);
for (var i = 0; i < this.count; i++) {
arr[i] = current.data;
current = current.next;
}
return arr;
}
}
}
and to check this use your console and try these line one by one.
>> var st = new Stack();
>> st.push("BP");
>> st.push("NK");
>> st.getTop();
>> st.getCount();
>> st.displayAll();
>> st.pop();
>> st.displayAll();
>> st.getTop();
>> st.peek();
I like to think that the cleanest way to implement stack and queues should be to use a container that allows addition and deletion from both ends and then limit its capabilities for one end which can be done through a simple array in Javascript.
// STATEMENTS USED IN STACK CONTAINER WHILE ENCAPSULATING
// Allow push and pop from the same end
array.push(element);
array.pop();
// STATEMENTS USED IN QUEUE CONTAINER WHILE ENCAPSULATING
// Allow push and pop from different ends
array.push(element);
array.shift();
A little late answer but i think this answer should be here. Here is an implementation of Queue with O(1) enqueue and O(1) dequeue using the sparse Array powers.
Sparse Arrays in JS are mostly disregarded but they are in fact a gem and we should put their power in use at some critical tasks.
So here is a skeleton Queue implementation which extends the Array type and does it's things in O(1) all the way.
class Queue extends Array {
constructor(){
super()
Object.defineProperty(this,"head",{ value : 0
, writable: true
});
}
enqueue(x) {
this.push(x);
return this;
}
dequeue() {
var first;
return this.head < this.length ? ( first = this[this.head]
, delete this[this.head++]
, first
)
: void 0; // perfect undefined
}
peek() {
return this[this.head];
}
}
var q = new Queue();
console.log(q.dequeue()); // doesn't break
console.log(q.enqueue(10)); // add 10
console.log(q.enqueue("DIO")); // add "DIO" (Last In Line cCc R.J.DIO reis cCc)
console.log(q); // display q
console.log(q.dequeue()); // lets get the first one in the line
console.log(q.dequeue()); // lets get DIO out from the line
.as-console-wrapper {
max-height: 100% !important;
}
So do we have a potential memory leak here? No i don't think so. JS sparse arrays are non contiguous. Accordingly deleted items shouln't be a part of the array's memory footprint. Let the GC do it's job for you. It's free of charge.
One potential problem is that, the length property grows indefinitely as you keep enqueueing items to the queue. However still one may implement an auto refreshing (condensing) mechanism to kick in once the length reaches to a certain value.
Edit:
The above code if just fine but the delete operator, while still being O(1), is a slow one. Besides the modern JS engines are so optimized that for like < ~25000 items .shift() works O(1) anyways. So we need something better.
In this particular case, as the engines develop we must harness their new powers. The code below uses a linked list and i believe it is the fastest and safest modern JS Queue structure as of 2021.
class Queue {
#head;
#last;
constructor(){
this.#head;
this.#last;
};
enqueue(value){
var link = {value, next: void 0};
this.#last = this.#head ? this.#last.next = link
: this.#head = link;
}
dequeue(){
var first;
return this.#head && ( first = this.#head.value
, this.#head = this.#head.next
, first
);
}
peek(){
return this.#head && this.#head.value;
}
};
This is an extremely fast Queue structure and uses Private Class Fields to hide critical variables from prying eyes.
If you're looking for ES6 OOP implementation of Stack and Queue data-structure with some basic operations (based on linked lists) then it may look like this:
Queue.js
import LinkedList from '../linked-list/LinkedList';
export default class Queue {
constructor() {
this.linkedList = new LinkedList();
}
isEmpty() {
return !this.linkedList.tail;
}
peek() {
if (!this.linkedList.head) {
return null;
}
return this.linkedList.head.value;
}
enqueue(value) {
this.linkedList.append(value);
}
dequeue() {
const removedHead = this.linkedList.deleteHead();
return removedHead ? removedHead.value : null;
}
toString(callback) {
return this.linkedList.toString(callback);
}
}
Stack.js
import LinkedList from '../linked-list/LinkedList';
export default class Stack {
constructor() {
this.linkedList = new LinkedList();
}
/**
* @return {boolean}
*/
isEmpty() {
return !this.linkedList.tail;
}
/**
* @return {*}
*/
peek() {
if (!this.linkedList.tail) {
return null;
}
return this.linkedList.tail.value;
}
/**
* @param {*} value
*/
push(value) {
this.linkedList.append(value);
}
/**
* @return {*}
*/
pop() {
const removedTail = this.linkedList.deleteTail();
return removedTail ? removedTail.value : null;
}
/**
* @return {*[]}
*/
toArray() {
return this.linkedList
.toArray()
.map(linkedListNode => linkedListNode.value)
.reverse();
}
/**
* @param {function} [callback]
* @return {string}
*/
toString(callback) {
return this.linkedList.toString(callback);
}
}
And LinkedList implementation that is used for Stack and Queue in examples above may be found on GitHub here.
var x = 10;
var y = 11;
var Queue = new Array();
Queue.unshift(x);
Queue.unshift(y);
console.log(Queue)
// Output [11, 10]
Queue.pop()
console.log(Queue)
// Output [11]
Single-ended queue
Here is a queue using a map. Since insertion order is guaranteed, you can iterate it like an array. Other than that the idea is very similar to Queue.js.
I've made some simple tests, but haven't tested it extensively. I also added some features that I thought were nice (constructing via an array) or easy to implement (e.g. last() and first()).
The simple version / intuition behind it is below:
class Queue {
constructor() {
this.offset = 0
this.data = new Map()
}
enqueue(item) {
const current = this.offset + this.length()
this.data.set(current, item)
}
dequeue() {
if (this.length() > 0) {
this.data.delete(this.offset)
this.offset += 1
}
}
first() {
return this.data.get(this.offset)
}
last() {
return this.data.get(this.offset + this.length() - 1)
}
length() {
return this.data.size
}
}
The issue with the simple version is that memory needs to be remapped when it has indexed over about 9 quadrillion (the value of Number.MAX_SAFE_INTEGER). Moreover, I think it might be nice to have array construction and it is nice to see the values being enqueued and dequeued being returned. One could account for this by writing the following code:
class Queue {
constructor() {
this.offset = 0
this.data = new Map()
if (arguments.length === 1) this._initializeFromArray(arguments[0])
}
enqueue(item) {
const current = this.offset + this.length()
this.data.set(current, item)
let result = this.data.get(current)
this._remapDataIfMaxMemoryViolation(current, Number.MAX_SAFE_INTEGER)
return result
}
dequeue() {
let result = undefined
if (this.length() > 0) {
result = this.data.get(this.offset)
this.data.delete(this.offset)
this.offset += 1
}
if (this.length() === 0) this.offset = 0
return result
}
first() {
return this.data.get(this.offset)
}
last() {
return this.data.get(this.offset + this.length() - 1)
}
length() {
return this.data.size
}
_remapDataIfMaxMemoryViolation(current, threshhold) {
if (current+1 === threshhold) {
const length = this.length()
this.offset = 0
for (const [key, value] of this.data) {
this.data.set(this.offset, value)
this.data.delete(key, value)
this.offset += 1
if (this.offset === length) break
}
}
}
_initializeFromArray(array) {
for (const value of array) {
this.data.set(this.offset, value)
this.offset += 1
}
}
}
I did some testing in the Chrome developer console with the following calls on the full version.
l = console.log // I'm lazy with typing
q = new Queue()
l('enqueue', q.enqueue(1))
l('enqueue', q.enqueue(2))
l('enqueue', q.enqueue(3))
l('enqueue', q.enqueue("hello"))
l('enqueue', q.enqueue("monkey"))
l('show 5 elements: ', q.data)
l('length', q.length())
l('first', q.first())
l('last', q.last())
l('dequeue', q.dequeue())
l('dequeue', q.dequeue())
l('show 3 elements', q.data)
q._remapDataIfMaxMemoryViolation(q.length()+q.offset-1, 5)
l('show 3 remapped elements', q.data)
l(queue = new Queue([3,4,5,6,7,8,9]))
l(queue.data)
Sorry to bump this topic but I scrolled over the many answers and did not see any implementation of an Object based Queue, which can perform enqueue AND dequeue with O(1) AND no wasted memory.
Dmitri Pavlutin has a nice starter code on his blog https://dmitripavlutin.com/javascript-queue/
It only misses a 0 length check, which is trivial to add.
the big and only problem of this solution is the ever growing index wich could hit some Number limit at one point, if the queue runs for a long time and/or at high speed (my intent is to process audio = high speed).
There is no perfect solution for this... the easy way can be resetting the index to 0 whenever the queue is empty.
At Last, I added a refactor method which costly shifts all the indexes back to the beginning, to use in the case the queue is never empty.
The performance is with no doubt better (the number is time in miliseconds for enqueuing 10 000 numbers then dequeuing them) :

class QueueObject {
constructor () {
this.data = {}
this.head = 0
this.tail = 0
this.length = 0
}
enqueue (value) {
this.data[this.tail++] = value
this.length++
}
dequeue () {
let value
if (this.length > 0) {
this.length--
value = this.data[this.head]
delete this.data[this.head++]
} else {
this.head = 0
this.tail = 0
value = null
}
return value
}
refactor () {
if (this.head > 0) {
for (let i = this.head; i < this.tail; i++) {
this.data[i - this.head] = this.data[i]
delete this.data[i]
}
this.tail = this.length
this.head = 0
}
}
}
An array is a stack in Javascript. Just use arr.push(x) and y = arr.pop().
Here is the simplest method for implementing a queue in Javascript that has an amortized time of O(1) for both enqueue(x) and y = dequeue(). It uses a mapping from insertion index to element.
function newQueue() {
return {
headIdx: 0,
tailIdx: 0,
elts: {},
enqueue: (elt) => queue.elts[queue.tailIdx++] = elt,
dequeue: () => {
if (queue.headIdx == queue.tailIdx) {
throw new Error("Queue is empty");
}
return queue.elts[queue.headIdx++];
},
size: () => queue.tailIdx - queue.headIdx,
isEmpty: () => queue.tailIdx == queue.headIdx
};
}
A queue implemented using a linked list is more efficient than this map-based method, and a queue implemented using a circular buffer is much more efficient than this map-based method, but the implementations of those two data structures is more complicated (especially the circular buffer data structure).
Regards,
In Javascript the implementation of stacks and queues is as follows:
Stack: A stack is a container of objects that are inserted and removed according to the last-in-first-out (LIFO) principle.
Queue: A queue is a container of objects (a linear collection) that are inserted and removed according to the first-in-first-out (FIFO) principle.
Unshift: Method adds one or more elements to the beginning of an array.
Shift: The method removes the first element from an array.
let stack = [];
stack.push(1);//[1]
stack.push(2);//[1,2]
stack.push(3);//[1,2,3]
console.log('It was inserted 1,2,3 in stack:', ...stack);
stack.pop(); //[1,2]
console.log('Item 3 was removed:', ...stack);
stack.pop(); //[1]
console.log('Item 2 was removed:', ...stack);
let queue = [];
queue.push(1);//[1]
queue.push(2);//[1,2]
queue.push(3);//[1,2,3]
console.log('It was inserted 1,2,3 in queue:', ...queue);
queue.shift();// [2,3]
console.log('Item 1 was removed:', ...queue);
queue.shift();// [3]
console.log('Item 2 was removed:', ...queue);
you can use WeakMaps for implementing private property in ES6 class and benefits of String propeties and methods in JavaScript language like below:
const _items = new WeakMap();
class Stack {
constructor() {
_items.set(this, []);
}
push(obj) {
_items.get(this).push(obj);
}
pop() {
const L = _items.get(this).length;
if(L===0)
throw new Error('Stack is empty');
return _items.get(this).pop();
}
peek() {
const items = _items.get(this);
if(items.length === 0)
throw new Error ('Stack is empty');
return items[items.length-1];
}
get count() {
return _items.get(this).length;
}
}
const stack = new Stack();
//now in console:
//stack.push('a')
//stack.push(1)
//stack.count => 2
//stack.peek() => 1
//stack.pop() => 1
//stack.pop() => "a"
//stack.count => 0
//stack.pop() => Error Stack is empty
I ran into this thread while implementing a BFS. After wondering why the performance was so poor I did some research. array.shift() typically runs in O(n) which increases my BFS runtime from O(V+E) to O(V^2+E).
Instead of implementing a queue from scratch I used the npm package double-ended-queue which is compatible to the previously used array methods and works like a charm. The deque can be used as stack or queue.
//import package
import Deque from 'double-ended-queue';
//create queue
let queue = new Deque();
//append
queue.push(item);
//dequeue (get first item inserted)
let firstItem = queue.shift();
//pop (get last item inserted)
let lastItem = queue.pop();
Well in case someone else needs it you can use this NPM package https://www.npmjs.com/package/data-structures-typescript, it has a queue and also and stack, it supports both javascript and typescript and it's generic so you can use it with your own type of value ;)
Construct a Queue using two Stacks.
O(1) for both enqueue and dequeue operations.
class Queue {
constructor() {
this.s1 = []; // in
this.s2 = []; // out
}
enqueue(val) {
this.s1.push(val);
}
dequeue() {
if (this.s2.length === 0) {
this._move();
}
return this.s2.pop(); // return undefined if empty
}
_move() {
while (this.s1.length) {
this.s2.push(this.s1.pop());
}
}
}