Explain Codes LogoExplain Codes Logo

How do you implement a Stack and a Queue in JavaScript?

javascript
data-structures
linked-list
queue
Alex KataevbyAlex KataevยทNov 10, 2024
โšกTLDR

In JavaScript, a Stack can be implemented using an array with push for appending and pop for removing elements. For implementing a Queue, you would similarly use push for adding but shift for removing elements to maintain FIFO order. Here are the quick examples:

// Stack: LIFO approach - Last hired, first fired! ๐Ÿ˜œ class Stack { constructor() { this.stack = []; } push(item) { this.stack.push(item); } // Hires an item pop() { return this.stack.pop(); } // Fires an item } // Queue: FIFO approach - Fairness prevails here! ๐Ÿ˜‡ class Queue { constructor() { this.queue = []; } enqueue(item) { this.queue.push(item); } // Adds an item to the service line dequeue() { return this.queue.shift(); } // Serves an item }

For both Stack and Queue operations, itโ€™s crucial to use native array methods: push/pop and enqueue/dequeue respectively.

Building with efficiency

While creating Stacks or Queues, your primary consideration should be towards efficiency, especially when youโ€™re handling a large number of elements. Here, let's understand some vital practices to boost performance and maintain best practices:

  • Stack operations: Stick with push and pop on arrays to ensure constant time complexity.

  • Queue performance: push and shift work well for queues, but shift can hamper performance on large arrays due to reindexing. A linked list or a pre-built library like Queue.js could be the rescuer here.

  • Reindexing operations: Stay away from unshift and splice, as they can lead to inefficient reindexing โ€“ use them with caution.

  • Safalra's principles: Resources like safalra.com can offer sophisticated strategies for queue management.

Implementing advanced data structures

When you encounter scenarios demanding more complex data structures, a simple array might not suffice to implement Stacks and Queues:

Linked list structures

For heavy lifting, a linked list can offer efficient insertion and deletion operations :

class Node { constructor(data) { this.data = data; this.next = null; } } // Node - A soldier in our data battalion class Queue { constructor() { this.head = null; this.tail = null; } enqueue(data) { let newNode = new Node(data); if (!this.head) { // Wise men said, "First come, first serve" this.head = this.tail = newNode; } else { this.tail.next = newNode; // There's always room for one more this.tail = newNode; } } dequeue() { if (!this.head) return null; // Oops! We've run out of items let data = this.head.data; this.head = this.head.next; // Keep moving forward if (!this.head) this.tail = null; // When there's no line, there's no tail return data; } }

Shunting-yard algorithm

Understanding how to correctly implement stacks and queues is fundamental to work with the shunting-yard algorithm, a popular method in expression parsing:

  • Stack: For holding operators and functions during the execution of the algorithm.
  • Queue: For holding the output in the correct order.

Efficiently printing contents

Debugging gets easier with visualization. Hereโ€™s how you can print the contents:

// For Stack printStack() { console.log(this.stack.join(' -> ')); } // For Queue printQueue() { let current = this.head; let output = ""; while (current) { output += current.data + " -> "; current = current.next; } console.log(output.slice(0, -4)); }

Addressing potential pitfalls

Here are some potential roadblocks and solutions:

  • Stack overflow: Keep your push calls under control to prevent infinite calls without pop.
  • Queue starvation: Make sure to structure your code to avoid an element waiting indefinitely in a queue.
  • Peek operations: It would be helpful to look at the next element to be removed without actually removing itโ€”use stack[stack.length - 1] or queue[0].

Common errors and how to avoid them

Here's how you can steer clear of common traps:

  • Relying on arrays' shift function for handling extensive queue operations can hamper your application's performance.
  • Make sure to check if the stack is empty before a pop operation to avoid running into errors.
  • Remember that arrays are zero-indexed when implementing a stack or a queue from the ground up.