Back to JavaScript
2026-03-138 min read

Stack & Queues using C (JavaScript)

Learn Stack & Queues using C (JavaScript) step by step with clear examples and exercises.

Why This Matters

Stack and queue data structures are fundamental building blocks in computer science, offering efficient solutions for managing and organizing data in various applications. In this extensive lesson, we delve deeper into the concepts of stacks and queues using C (JavaScript), exploring practical examples, common mistakes, and interview-ready scenarios.

Importance of Data Structures

Data structures help organize and manage data efficiently, impacting the performance and scalability of applications. By choosing appropriate data structures for specific problems, developers can write faster, more efficient code.

Prerequisites

To follow this lesson, you should have a solid understanding of the following concepts:

  1. Basic JavaScript syntax (variables, functions, loops, conditional statements)
  2. Arrays and objects in JavaScript
  3. Understanding of data structures and algorithms
  4. Familiarity with ES6 class syntax and arrow functions
  5. Knowledge of Big O notation to understand the time complexity of various operations

Prerequisite Explanation

Before diving into stacks and queues, it's essential to have a good grasp of the basics of JavaScript programming. Knowledge of arrays and objects is crucial for understanding how these data structures work internally. Additionally, understanding data structures and algorithms will help you appreciate the benefits of using stacks and queues in your code.

Core Concept

Stacks

A stack is a linear data structure that follows the Last-In-First-Out (LIFO) principle. It can be thought of as a pile of dishes where you only add or remove dishes from the top.

Properties of a Stack

  1. Push: Add an item to the top of the stack.
  2. Pop: Remove and return the top item from the stack.
  3. Peek: Return the top item without removing it.
  4. IsEmpty: Check if the stack is empty.
  5. Size: Determine the number of items in the stack.
  6. Min: Find the minimum value at the bottom of the stack (only applicable for numerical data).
  7. Max: Find the maximum value at the top of the stack (only applicable for numerical data).

Implementing a Stack in JavaScript

class Stack {
constructor() {
this.items = [];
}

push(item) {
this.items.push(item);
}

pop() {
if (this.isEmpty()) {
throw new Error("Stack is empty");
}
return this.items.pop();
}

peek() {
if (this.isEmpty()) {
throw new Error("Stack is empty");
}
return this.items[this.items.length - 1];
}

isEmpty() {
return this.items.length === 0;
}

size() {
return this.items.length;
}

min() {
if (this.isEmpty()) {
throw new Error("Stack is empty");
}
const minValue = Math.min(...this.items);
return minValue;
}

max() {
if (this.isEmpty()) {
throw new Error("Stack is empty");
}
const maxValue = Math.max(...this.items);
return maxValue;
}
}

Queues

A queue is a linear data structure that follows the First-In-First-Out (FIFO) principle. It can be thought of as a line at a bank, where people join the end and are served from the front.

Properties of a Queue

  1. Enqueue: Add an item to the back of the queue.
  2. Dequeue: Remove and return the front item from the queue.
  3. Peek: Return the front item without removing it.
  4. IsEmpty: Check if the queue is empty.
  5. Size: Determine the number of items in the queue.
  6. Front: Find the front item (only applicable for numerical data).
  7. Rear: Find the rear item (only applicable for numerical data).

Implementing a Queue in JavaScript

class Queue {
constructor() {
this.items = [];
}

enqueue(item) {
this.items.push(item);
}

dequeue() {
if (this.isEmpty()) {
throw new Error("Queue is empty");
}
return this.items.shift();
}

peek() {
if (this.isEmpty()) {
throw new Error("Queue is empty");
}
return this.items[0];
}

isEmpty() {
return this.items.length === 0;
}

size() {
return this.items.length;
}

front() {
if (this.isEmpty()) {
throw new Error("Queue is empty");
}
return this.items[0];
}

rear() {
if (this.isEmpty()) {
throw new Error("Queue is empty");
}
return this.items[this.items.length - 1];
}
}

Worked Example

Let's implement a simple web application that uses a stack to handle browser history and a queue to manage user requests.

class BrowserHistory {
constructor() {
this.stack = new Stack();
this.currentPage = "";
this.requestQueue = new Queue();
}

goBack() {
if (this.stack.isEmpty()) {
throw new Error("No previous pages");
}
const previousPage = this.stack.pop();
this.currentPage = previousPage;
}

goForward() {
if (this.stack.size() === 1 && this.stack.peek() !== this.currentPage) {
// If there is only one page on the stack and it's not the current page, an error has occurred
throw new Error("An error has occurred");
}
if (this.stack.isEmpty()) {
this.stack.push(this.currentPage);
}
const nextPage = this.currentPage;
this.currentPage = this.stack.peek();
this.stack.push(nextPage);
}

visitPage(page) {
if (!this.isEmpty()) {
// If there are pages on the stack, push the current page before visiting the new one
const previousPage = this.currentPage;
this.stack.push(previousPage);
}
this.currentPage = page;
this.requestQueue.enqueue(page);
}

nextRequest() {
return this.requestQueue.dequeue();
}

minPage() {
if (this.isEmpty()) {
throw new Error("No pages in history");
}
const minPage = this.stack.min();
return minPage;
}

maxPage() {
if (this.isEmpty()) {
throw new Error("No pages in history");
}
const maxPage = this.stack.max();
return maxPage;
}
}

const browserHistory = new BrowserHistory();
browserHistory.visitPage("homepage");
console.log(browserHistory.nextRequest()); // undefined (since no request has been made yet)
browserHistory.goForward(); // Stack: ["homepage"] Current Page: undefined
browserHistory.visitPage("aboutus");
console.log(browserHistory.nextRequest()); // "homepage"
browserHistory.goBack(); // Stack: ["homepage"] Current Page: "aboutus"

Common Mistakes

  1. Not checking for empty stacks or queues before performing operations: This can lead to runtime errors and unexpected behavior.
  2. Misusing the stack or queue for the wrong problem: Understanding when to use a stack or queue is crucial for solving problems efficiently.
  3. Not properly managing memory: In some cases, improper management of memory can cause performance issues or memory leaks.
  4. Implementing inefficient algorithms: Always strive for optimized solutions that minimize time and space complexity.
  5. Not considering the order of operations when using stacks: The order of operations is important when performing complex calculations on a stack, as it follows LIFO (Last-In-First-Out).
  6. Not properly handling exceptions when working with empty stacks or queues: Proper error handling is essential to ensure that your code remains robust and easy to maintain.

Common Mistakes - Additional Examples

  1. Using a stack instead of a queue for a problem requiring FIFO behavior: This will result in incorrect output or unexpected errors.
  2. Not properly handling exceptions when working with empty stacks or queues: Proper error handling is essential to ensure that your code works correctly in all situations.
  3. Implementing custom data structures without considering the time and space complexity: Always consider the efficiency of your implementations, as this can significantly impact the performance of your applications.
  4. Not testing edge cases: Testing various scenarios, including empty stacks or queues, single items, and multiple items, will help ensure that your code works correctly in all situations.
  5. Using a stack for a problem that requires frequent removal of elements from the middle or random access: Stacks are not suitable for such problems as they only allow access to elements from one end (the top).
  6. Using a queue for a problem that requires frequent insertion and removal of elements from both ends: Queues are not suitable for such problems, as they only allow access to elements from one end (the front).

Practice Questions

  1. Implement a JavaScript function to check if a given string is a valid expression using two stacks.
  2. Given an array of integers, implement a JavaScript function that finds the maximum sum of any contiguous subarray using a queue.
  3. Implement a JavaScript function that determines whether a given graph has a cycle using Depth-First Search (DFS) and a stack.
  4. Write a JavaScript program to implement a simple web server using a queue to manage incoming requests.
  5. Given an array of parentheses, implement a JavaScript function that checks if the input is balanced using a stack.
  6. Implement a JavaScript function that implements the Tower of Hanoi problem using three stacks.
  7. Write a JavaScript program to implement a postfix expression evaluator using a stack.
  8. Given an array of strings, implement a JavaScript function that sorts the array by the length of each string using a stack.
  9. Implement a JavaScript function that finds the first non-repeating character in a given string using two stacks.
  10. Write a JavaScript program to implement a simple game of Simon Says using a queue for user inputs and a stack to store the correct sequence.

FAQ

  1. Why use stacks and queues instead of arrays or lists?
  • Stacks and queues provide specific operations like push, pop, peek, enqueue/dequeue that are optimized for certain algorithms and problem-solving scenarios. They also offer additional properties such as min, max, front, and rear.
  1. What is the time complexity of common stack and queue operations in JavaScript?
  • Push, pop, peek, enqueue, and dequeue operations have an average time complexity of O(1) in both stacks and queues. Min, max, front, and rear operations have an average time complexity of O(n) in the worst case (when all items are unique).
  1. Can I implement a stack or queue using arrays in JavaScript?
  • Yes! In fact, the provided implementations use arrays under the hood to store the data.
  1. What is the difference between a stack and a queue in terms of their operations?
  • A stack follows LIFO (Last-In-First-Out) while a queue follows FIFO (First-In-First-Out). This means that items are added and removed from different ends for each data structure.
  1. What is the time complexity of searching an item in a stack or queue?
  • Since stacks and queues do not have a built-in search operation, their time complexity for searching an item is O(n), where n is the number of items in the data structure. However, this can be optimized by using additional data structures like hash tables or binary search trees.
  1. Can I use a stack to implement a queue?
  • Yes! A stack can be used as an implementation for a queue by pushing elements onto the stack when enqueueing and popping elements off the stack while dequeuing, but always removing the top item (the last element added). This approach, however, may not offer the same time complexity benefits as using a dedicated queue data structure.
  1. Can I use a queue to implement a stack?
  • Yes! A queue can be used as an implementation for a stack by dequeuing elements until there is only one element left (the top item), and then enqueueing that element again when pushing. This approach, however, may not offer the same time complexity benefits as using a dedicated stack data structure.
Stack & Queues using C (JavaScript) | JavaScript | XQA Learn