Queue

Posted by Dustin Boston in .

A linear collection following the First-In-First-Out (FIFO) principle, supporting operations like enqueue and dequeue, useful in task scheduling.

  • Time complexity for access and search is Linear, i.e. O(n)
  • Time complexity for insertion and deletion is Constant, i.e. O(n)
  • Space complexity is Linear

Source Code Listing

code.ts

/**
 * @file Queue
 * @author Dustin Boston
 * @see Cormen, T, Leiserson, C, Rivest, R, and Stein, C. (2001).
 *   Introduction to Algorithms. (2nd ed). The MIT Press.
 */

/**
 * Represents a Queue data structure with basic operations such as enqueue and
 * dequeue.
 */
export class Queue<T> {
  queue: Array<T | undefined>;

  /** Index of next element to dequeue */
  head: number | undefined = undefined;

  /** Index of next element to enqueue */
  tail = 0;

  /**
   * Creates an instance of a Queue from an existing array.
   *
   * @param array - The initial array to be converted into a queue.
   */
  constructor(array: T[] = []) {
    this.queue = array;
  }

  isEmpty(): boolean {
    return this.head === undefined;
  }

  isFull(): boolean {
    return this.head === this.tail;
  }

  /**
   * Adds an element to the end of the queue. O(1)
   *
   * @param value - The element to be added to the queue.
   */
  enqueue(value: T): void {
    if (this.isFull()) {
      throw new Error("Queue is already at capacity.");
    }

    this.queue[this.tail] = value;

    if (this.head === undefined) {
      this.head = this.tail;
    }

    this.tail = this.tail === this.queue.length - 1 ? 0 : this.tail + 1;
  }

  /**
   * Removes and returns the element at the front of the queue. O(1)
   *
   * @returns The element removed from the front of the queue.
   */
  dequeue(): T | undefined {
    if (this.head === undefined) {
      throw new Error("Cannot dequeue from an empty queue.");
    }

    const value = this.queue[this.head];
    this.queue[this.head] = undefined;

    this.head = this.head === this.queue.length - 1 ? 0 : this.head + 1;

    if (this.head === this.tail) {
      this.head = undefined;
    }

    return value;
  }
}

export function printQueue<T>(queueToPrint: Queue<T>) {
  return {
    head: queueToPrint.head,
    tail: queueToPrint.tail,
    queue: queueToPrint.queue,
  };
}

code_test.ts

import {test, expect} from "bun:test";
import {Queue, printQueue} from "./code";

function print(queue: Queue<number>) {
  return JSON.stringify(printQueue(queue));
}

test("testEnqueue", () => {
  const queue = new Queue<number>(Array.from({length: 2}));
  queue.enqueue(1);
  queue.enqueue(2);

  expect(print(queue)).toBe(`{"head":0,"tail":0,"queue":[1,2]}`);
});

test("testDequeue", () => {
  const queue = new Queue<number>(Array.from({length: 3}));
  queue.enqueue(1);
  queue.enqueue(2);
  queue.enqueue(3);

  queue.dequeue();
  queue.dequeue();
  queue.dequeue();

  expect(print(queue)).toBe(`{"tail":0,"queue":[null,null,null]}`);
});

test("testUnderflow", () => {
  const queue = new Queue<number>(Array.from({length: 3}));
  queue.enqueue(1);
  queue.enqueue(2);
  queue.enqueue(3);
  queue.dequeue();
  queue.dequeue();
  queue.dequeue();

  expect(() => queue.dequeue()).toThrow();
});