DSA: Queues

Queue

Queues are linear data structures like array. They support:

  1. First In, First Out" or FIFO principle

Enqueue: Adding an element at the back of the queue.

Dequeue: Removing an element from the front of the queue.

Queue

Types of Queues:

Simple Queue: This is the most basic type. Elements are inserted at the rear and removed from the front, following the FIFO principle.

Deque (Double Ended Queue): Here, elements can be added or removed from both ends of the Queue.

Circular Queue: the last element points to the first element, making a loop!

Priority Queue: In this type, not all elements are equal. Some are considered more important and get to jump the line.

Affinity Queue: In this type, every element has an affinity & is placed with an element having the same affinity; otherwise placed at the rear.

Queue

Operations on Simple Queues:

 

Enqueue: Adding an element at the back of the queue.

Dequeue: Removing an element from the front of the queue.

Peek: see the first element in the Queue

IsEmpty: check if queue is empty

Queue

Node

Node

Node

Node

Queue

Queue

Implementing a Simple Queue:

 

We will be implementing a simple Queue, of finite size using a Linked List.

It will be implemented in 2 parts:

  1. Node Class: This is an inner class used to represent each element in the queue. Each node contains the data and a reference to the next node in the list.

  2. Queue Class Attributes:

    • front: A reference to the first node in the queue.
    • rear: A reference to the last node in the queue.
    • size: An integer tracking the number of elements in the queue.
  3. Constructor:

    • The constructor initialises an empty queue where both front and rear are set to null, and size is set to 0.
class Node {
    // Node class for storing data and 
    // the reference to the next node
    constructor(data) {
        this.data = data;
        this.next = null;
    }
}
class Queue {
    // Queue class using linked list
    constructor() {
        this.front = null;
        this.rear = null;
        this.size = 0;
    }
}

Queue

Enqueue Operation (enqueue method):

  • This method adds an element to the end of the queue.
  • A new node is created with the given data.
  • If the queue is empty (rear == null), both front and rear point to the new node.
  • If the queue is not empty, the new node is added after rear, and then rear is updated to point to this new node.
  • The size of the queue is incremented.
// Add an element to the rear of the queue
    enqueue(data) {
        let newNode = new Node(data);
        if (this.rear === null) { // queue is empty
            this.front = this.rear = newNode;
        } else {
// adding the new node in the next of current rear node
            this.rear.next = newNode; 
          
//. changing the current rear node with the new one
            this.rear = newNode; 
        }
        this.size++;
    }

Queue

Dequeue Operation (dequeue method):

  • This method removes an element from the front of the queue.
  • If the queue is empty (front == null), the method returns null.
  • The data from the front node is saved.
  • front is updated to point to the next node in the queue.
  • If the queue becomes empty after this operation (front == null), rear is also set to null.
  • The size of the queue is decremented.
  • The method returns the saved data.
// Remove an element from the front of the queue
    dequeue() {
        if (this.front === null) {
            return null;
        }
        let temp = this.front;
        this.front = this.front.next;
        /** when queue has only 1 element */
        if (this.front === null) {
            this.rear = null;
        }
        this.size--;
        return temp.data;
    }

Queue

Utility Methods:
peek:

This method returns the data from the front node without removing it.
If the queue is empty (front == null), it returns null.
isEmpty: Checks if the queue is empty (i.e., size == 0).

size: Returns the number of elements in the queue.

// Get the front element of the queue
    peek() {
        /** when queue is empty */
        if (this.front === null) {
            return null;
        }
        return this.front.data;
    }

    // Check if the queue is empty
    isEmpty() {
        return this.size === 0;
    }

    // Get the number of elements in the queue
    getSize() {
        return this.size;
    }

Queue

Implementing a Circular Queue

We will be implementing a simple Queue, of finite size using a Linked List.

It will be implemented in 2 parts:

Node Class: This is an inner class used to represent each element in the queue. Each node contains the data and a reference to the next node in the list.

Queue Class Attributes:

front: A reference to the first node in the queue.
rear: A reference to the last node in the queue.
size: An integer tracking the number of elements in the queue.
Constructor:

The constructor initialises an empty queue where both front and rear are set to null, and size is set to 0.

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

class CircularQueue {
    constructor(capacity) {
        this.capacity = capacity;
        this.head = null;
        this.tail = null;
        this.size = 0;
    }
}

Queue Questions

Step-by-step solution walkthrough:

  1. Initialize an empty stack.
  2. While the queue is not empty, dequeue an element from the queue and push it onto the stack.
  3. Once all the elements have been transferred to the stack, pop elements from the stack and enqueue them back to the queue until the stack is empty.
  4. The queue now contains the elements in reversed order.

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 1: Reverse a Queue

Queue Questions

Problem 4: Palindrome Check using Queue

  1. Normalize the String: Remove any spaces, punctuation, and convert all characters to the same case (lowercase or uppercase) to ensure consistency in comparison.
  2. Initialize a Queue: Create an empty queue that will be used to store characters of the string.
  3. Enqueue Characters: Iterate over the normalized string and enqueue each character into the queue.
  4. Check for Palindrome:
    Dequeue a character from the front and end of the queue.
    Compare these two dequeued characters.
    If at any point the two characters do not match, return false, indicating the string is not a palindrome.
    Repeat step-4, until there are less than 2 characters left in the queue.
  5. End of Queue: If you've iterated through the queue without finding a mismatch, return true, indicating the string is a palindrome.

Queue Questions

Problem 4: Palindrome Check using Queue

Queue Questions

Problem 4: Palindrome Check using Queue

Queue Questions

Problem 4: Palindrome Check using Queue

Queue Questions

Problem 4: Palindrome Check using Queue

Queue Questions

Problem 4: Palindrome Check using Queue

Queue Questions

Problem 4: Palindrome Check using Queue

Queue Questions

Problem 5: Zigzag Iterator

  1. Initialize the queue and insert the iterators of v1 and v2 only if they have elements.
  2. In the next() function, pop an iterator from the front of the queue. Get the next element from the iterator, then re-insert the iterator back into the queue if there are still elements left in the iterator.
  3. In the hasNext() function, simply check if the queue is empty.

Queue Questions

Problem 6: Max of All Subarrays of Size 'k'

  1. Initialize a Deque: Create an empty deque (double-ended queue) that will be used to store the indices of array elements. This deque will help us maintain the potential maximum elements for each subarray.
  2. Process the First 'k' Elements: Iterate through the first 'k' elements of the array.
    For each element, while the deque is not empty and the last element in the deque is less than or equal to the current element, remove the last element from the deque. This step ensures that the deque contains elements in decreasing order.
    Add the current element's index to the rear of the deque.
  3. Process the Remaining Elements of the Array: For each element in the array starting from the 'k'th element:
    Add the element at the front of the deque to the result, as it represents the maximum of the previous subarray.
    Remove the indices from the front of the deque if they are out of the current window (i.e., if the index is less than the current index - 'k').
    Similar to step 2, remove elements from the rear of the deque if they are smaller than or equal to the current element, as they cannot be the maximum for the current or future windows.
    Add the current element's index to the rear of the deque.
  4. Return the Result: The result contains the maximum of each subarray of size 'k'.

Queue Questions

Problem 6: Max of All Subarrays of Size 'k'

DSA: Queues

By Yash Priyam

DSA: Queues

  • 140