Beginner
18 min
Full Guide

Queues

Queue data structure, FIFO principle, and implementation

What is a Queue?

A queue is a linear data structure that follows the First In, First Out (FIFO) principle. Think of it like a line of people waiting - the first person to join the line is the first one to be served. Elements are added at the back (enqueue) and removed from the front (dequeue).

Queues are essential for managing tasks in order, handling requests, and implementing breadth-first search algorithms.

FIFO Principle

← Dequeue (Front)
[1]
[2]
[3]
Enqueue (Rear) →

First In, First Out - Like a waiting line!

Queue Operations

enqueue(element) - O(1)

Add an element to the rear/back of the queue.

dequeue() - O(1)

Remove and return the front element from the queue.

front() / peek() - O(1)

Return the front element without removing it.

isEmpty() - O(1)

Check if the queue is empty.

size() - O(1)

Return the number of elements in the queue.

Queue Implementation Using Array

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

  // Add element to rear - O(1)
  enqueue(element) {
    this.items.push(element);
  }

  // Remove element from front - O(n) due to array shift
  dequeue() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.items.shift();  // O(n) operation!
  }

  // Get front element - O(1)
  front() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.items[0];
  }

  // Check if empty - O(1)
  isEmpty() {
    return this.items.length === 0;
  }

  // Get size - O(1)
  size() {
    return this.items.length;
  }

  // Print queue
  print() {
    console.log(this.items.join(' ← '));
  }
}

// Usage
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.print();  // 1 ← 2 ← 3

console.log(queue.dequeue());  // 1
console.log(queue.front());    // 2
queue.print();  // 2 ← 3

Note: Array.shift() is O(n) because it needs to shift all elements. For better performance, use a circular queue or linked list implementation.

Optimized Queue Using Object (O(1) dequeue)

class OptimizedQueue {
  constructor() {
    this.items = {};
    this.frontIndex = 0;
    this.backIndex = 0;
  }

  // Enqueue - O(1)
  enqueue(element) {
    this.items[this.backIndex] = element;
    this.backIndex++;
  }

  // Dequeue - O(1)
  dequeue() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    
    const item = this.items[this.frontIndex];
    delete this.items[this.frontIndex];
    this.frontIndex++;
    
    return item;
  }

  // Front - O(1)
  front() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.items[this.frontIndex];
  }

  isEmpty() {
    return this.backIndex === this.frontIndex;
  }

  size() {
    return this.backIndex - this.frontIndex;
  }

  print() {
    const values = [];
    for (let i = this.frontIndex; i < this.backIndex; i++) {
      values.push(this.items[i]);
    }
    console.log(values.join(' ← '));
  }
}

// This is much more efficient for large queues!

Queue Implementation Using Linked List

class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class LinkedQueue {
  constructor() {
    this.front = null;
    this.rear = null;
    this.length = 0;
  }

  // Enqueue - O(1)
  enqueue(data) {
    const newNode = new Node(data);
    
    if (this.isEmpty()) {
      this.front = this.rear = newNode;
    } else {
      this.rear.next = newNode;
      this.rear = newNode;
    }
    
    this.length++;
  }

  // Dequeue - O(1)
  dequeue() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    
    const dequeuedData = this.front.data;
    this.front = this.front.next;
    
    if (this.front === null) {
      this.rear = null;
    }
    
    this.length--;
    return dequeuedData;
  }

  peek() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.front.data;
  }

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

  size() {
    return this.length;
  }
}

Circular Queue

A circular queue is a linear data structure that connects the end back to the beginning, forming a circle. This efficiently uses space when implementing a queue with a fixed size array.

class CircularQueue {
  constructor(capacity) {
    this.items = new Array(capacity);
    this.capacity = capacity;
    this.front = -1;
    this.rear = -1;
    this.currentSize = 0;
  }

  // Check if full - O(1)
  isFull() {
    return this.currentSize === this.capacity;
  }

  // Check if empty - O(1)
  isEmpty() {
    return this.currentSize === 0;
  }

  // Enqueue - O(1)
  enqueue(element) {
    if (this.isFull()) {
      throw new Error('Queue is full');
    }
    
    // First element
    if (this.front === -1) {
      this.front = 0;
    }
    
    // Circular increment
    this.rear = (this.rear + 1) % this.capacity;
    this.items[this.rear] = element;
    this.currentSize++;
  }

  // Dequeue - O(1)
  dequeue() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    
    const item = this.items[this.front];
    
    // Reset if last element
    if (this.front === this.rear) {
      this.front = this.rear = -1;
    } else {
      // Circular increment
      this.front = (this.front + 1) % this.capacity;
    }
    
    this.currentSize--;
    return item;
  }

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

  size() {
    return this.currentSize;
  }
}

// Usage
const cq = new CircularQueue(5);
cq.enqueue(1);
cq.enqueue(2);
cq.enqueue(3);
console.log(cq.dequeue());  // 1
cq.enqueue(4);
cq.enqueue(5);
cq.enqueue(6);  // Now using the circular space!

Priority Queue

A priority queue is a special type of queue where each element has a priority. Elements with higher priority are dequeued before elements with lower priority.

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

  // Enqueue with priority - O(n) for simple implementation
  enqueue(element, priority) {
    const queueElement = { element, priority };
    
    // Find correct position based on priority
    let added = false;
    for (let i = 0; i < this.items.length; i++) {
      if (queueElement.priority < this.items[i].priority) {
        this.items.splice(i, 0, queueElement);
        added = true;
        break;
      }
    }
    
    if (!added) {
      this.items.push(queueElement);
    }
  }

  // Dequeue highest priority - O(1)
  dequeue() {
    if (this.isEmpty()) {
      throw new Error('Queue is empty');
    }
    return this.items.shift().element;
  }

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

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

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

  print() {
    console.log(this.items.map(item => 
      `${item.element}(p:${item.priority})`
    ).join(' ← '));
  }
}

// Usage
const pq = new PriorityQueue();
pq.enqueue('Low priority task', 3);
pq.enqueue('High priority task', 1);
pq.enqueue('Medium priority task', 2);
pq.print();  // High priority task(p:1) ← Medium priority task(p:2) ← Low priority task(p:3)
console.log(pq.dequeue());  // 'High priority task'

Note: For better performance, priority queues are typically implemented using heaps, which allow O(log n) enqueue and dequeue operations.

Common Queue Problems

1. Implement Stack Using Queues

class StackUsingQueues {
  constructor() {
    this.q1 = [];
    this.q2 = [];
  }

  push(x) {
    // Add to q2
    this.q2.push(x);
    
    // Move all from q1 to q2
    while (this.q1.length > 0) {
      this.q2.push(this.q1.shift());
    }
    
    // Swap q1 and q2
    [this.q1, this.q2] = [this.q2, this.q1];
  }

  pop() {
    if (this.empty()) {
      throw new Error('Stack is empty');
    }
    return this.q1.shift();
  }

  top() {
    if (this.empty()) {
      throw new Error('Stack is empty');
    }
    return this.q1[0];
  }

  empty() {
    return this.q1.length === 0;
  }
}

2. Generate Binary Numbers from 1 to N

function generateBinaryNumbers(n) {
  const result = [];
  const queue = [];
  
  queue.push('1');
  
  for (let i = 0; i < n; i++) {
    const front = queue.shift();
    result.push(front);
    
    // Generate next binary numbers
    queue.push(front + '0');
    queue.push(front + '1');
  }
  
  return result;
}

console.log(generateBinaryNumbers(5));
// ['1', '10', '11', '100', '101']

3. First Non-Repeating Character in Stream

function firstNonRepeating(stream) {
  const queue = [];
  const charCount = {};
  const result = [];
  
  for (let char of stream) {
    // Update count
    charCount[char] = (charCount[char] || 0) + 1;
    queue.push(char);
    
    // Remove repeating characters from front
    while (queue.length > 0 && charCount[queue[0]] > 1) {
      queue.shift();
    }
    
    // First non-repeating or -1
    result.push(queue.length > 0 ? queue[0] : '-1');
  }
  
  return result;
}

console.log(firstNonRepeating('aabcbc'));
// ['a', '-1', 'b', 'b', 'b', 'c']

Real-World Applications

🖨️ Printer Queue

Print jobs are processed in the order they're received using a queue.

💻 Task Scheduling

Operating systems use queues to manage process scheduling and task execution.

🔍 BFS Algorithm

Breadth-First Search uses a queue to explore nodes level by level.

📞 Call Center

Customer service calls are handled in the order they're received.

🎮 Game Turn System

Multiplayer games use queues to manage player turns and actions.

📨 Message Queue

Asynchronous message processing systems (RabbitMQ, Kafka) use queues.

Queue vs Stack Comparison

Aspect Queue (FIFO) Stack (LIFO)
Principle First In, First Out Last In, First Out
Insert/Remove Insert at rear, remove from front Both at top
Operations enqueue(), dequeue() push(), pop()
Use Case BFS, task scheduling DFS, function calls, undo
Real World Waiting line Stack of plates

💡 Key Takeaways

  • ✓ Queue follows FIFO (First In, First Out) principle
  • ✓ All operations are O(1) with proper implementation
  • ✓ Use linked list or optimized object for O(1) dequeue
  • ✓ Circular queues efficiently use fixed-size arrays
  • ✓ Priority queues order elements by priority, not arrival time
  • ✓ Essential for BFS, task scheduling, and message processing