DEV Community

Masaki Fukunishi
Masaki Fukunishi

Posted on

Understanding Queue: Features, Time Complexity, and Implementation

This article is based on a course in the following repository.
https://github.com/cookie777/altgorithm-data-structure-crash-course/blob/main/queue.md

What is Queue?

A queue is a data structure that follows the First-In-First-Out (FIFO) principle. It has two main operations.

  • Enqueue

    This operation adds an element to the end of the queue.

  • Dequeue

    This operation removes the first element from the queue.

What is difference between Queue and Array. Pros and Cons

Queue

Pros

  • Dynamic size

  • Efficient deletion

    Unlike Array, Queue does not have to shift all the elements when deliting an element at the beginning of the queue.

Cons

  • Random access is not supported

Array

Pros

  • Random access is supported

Cons

  • Fixed size

  • Inefficient deletion

    Unlike Queue, Array has to shift all the elements when deliting an element at the beginning of the array.

Can you come up with when to use Queue?

  • Printer

    In a printing system, a queue is used to manage multiple print jobs, ensuring they are printed in the order they were requested.

  • Asynchronous processing

    In applications where tasks can take varying amounts of time to complete, using a queue can decouple task submission from task execution.

  • Stackable Pencil

    In a Stackable Pencil, the first piece inserted into the pencil is the first one to be removed.

Analysis

What is the time complexity of following operations for Queue? Why?
Since they are multiple types of queue, consider a type using linked list.

Basics

enqueue (add an element to the Queue)

  • O(1)

    Adding an element to the last of the queue requires updating only the last pointer, which takes constant time.

dequeue (remove and return an element from the Queue)

  • O(1)

    Removing an element from the first of the queue requires updating only the first pointer, which takes constant time.

peek (return an element from the Queue)

  • O(1)

    Returning an element from the first of the queue is just accessing the first pointer, which takes constant time.

size (get the size of the Queue)

  • O(1)

    Getting the size of the queue is just accessing the size property, which takes constant time.

merge two Queue

  • O(1)

    Merging two queues is just updating the last pointer of the first queue to the first pointer of the second queue, which takes constant time.

Comparison

What if we try to implement Queue with Array instead of Linked List?
What would be the difference?

Memory Usage

Linked lists require additional memory to store pointers for linking elements, while arrays can store elements in a contiguous block of memory.

Dynamic vs. Static Size

Linked lists can grow dynamically without requiring resizing operations, while arrays have a fixed size and require resizing operations when the capacity is exceeded.

Dequeue

Linked lists are more efficient than arrays when deleting an element at the beginning of the queue, since they do not have to shift all the elements.

Exercise

Exercise 01

Let's try to implement your own Queue with using Linked-List!

class ListNode {
  val: number | null;
  next: ListNode | null;

  constructor(val: number | null = null, next: ListNode | null = null) {
    this.val = val;
    this.next = next;
  }
}

class MyQueue {
  private head: ListNode | null;
  private tail: ListNode | null;
  private length: number;

  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  enqueue(value: number) {
    const newNode = new ListNode(value);
    if (this.length === 0) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      if (!this.tail) throw new Error("Unexpected: The tail is null.");
      this.tail.next = newNode;
      this.tail = newNode;
    }
    this.length++;
  }

  dequeue(): number | null {
    if (!this.head) return null;
    if (this.head === this.tail) {
      this.tail = null;
    }
    const holdingPointer = this.head;
    this.head = this.head.next;
    this.length--;
    return holdingPointer.val;
  }

  peek(): number | null {
    if (!this.head) return null;
    return this.head.val;
  }

  size(): number {
    return this.length;
  }
}

const myQueue = new MyQueue();
myQueue.enqueue(1);
myQueue.enqueue(2);
myQueue.enqueue(3);
console.log(myQueue.dequeue()); // 1
console.log(myQueue.peek()); // 2
console.log(myQueue.size()); // 2
console.log(myQueue);
Enter fullscreen mode Exit fullscreen mode

Exercise 02

Let's try to solve some leet code questions

class LinkedListNode {
  val: number | null;
  next: LinkedListNode | null;

  constructor(val: number | null = null, next: LinkedListNode | null = null) {
    this.val = val;
    this.next = next;
  }
}

class MyQueue {
  private head: LinkedListNode | null;
  private tail: LinkedListNode | null;
  private length: number;

  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  enqueue(value: number) {
    const newNode = new LinkedListNode(value);
    if (this.length === 0) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      if (!this.tail) throw new Error("Unexpected: The tail is null.");
      this.tail.next = newNode;
      this.tail = newNode;
    }
    this.length++;
  }

  dequeue(): number | null {
    if (!this.head) return null;
    if (this.head === this.tail) {
      this.tail = null;
    }
    const holdingPointer = this.head;
    this.head = this.head.next;
    this.length--;
    return holdingPointer.val;
  }

  peek(): number | null {
    if (!this.head) return null;
    return this.head.val;
  }

  size(): number {
    return this.length;
  }
}

class RecentCounter {
  private requests: MyQueue;

  constructor() {
    this.requests = new MyQueue();
  }

  ping(t: number): number {
    this.requests.enqueue(t);
    while (this.requests.peek()) {
      const head = this.requests.peek()
      if (!head || head >= t - 3000) {
        break
      }
      this.requests.dequeue();
    }
    return this.requests.size();
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)