UNPKG

algojs-dsa

Version:

High-performance data structures (Deque, PriorityQueue) for DSA practice with optimal time complexity

417 lines (416 loc) 10.7 kB
/** * A high-performance Deque (Double-Ended Queue) implementation. * Supports Stack, Queue, and Deque operations with O(1) time complexity. * * @template T The type of elements in the deque * * @example * ```typescript * const deque = new Deque<number>(); * * // Use as Stack * deque.push(1); * deque.push(2); * deque.pop(); // 2 * * // Use as Queue * deque.enqueue(1); * deque.enqueue(2); * deque.dequeue(); // 1 * * // Use as Deque * deque.pushFront(1); * deque.pushBack(2); * deque.popFront(); // 1 * deque.popBack(); // 2 * ``` */ class Deque { /** * Creates a new Deque instance. * * @param initialCapacityOrElements - Initial capacity or array of elements * @throws {Error} If initialCapacity is less than 1 * * @timeComplexity O(n) if elements provided, O(1) otherwise * @spaceComplexity O(capacity) */ constructor(initialCapacityOrElements) { if (Array.isArray(initialCapacityOrElements)) { // Initialize with elements const elements = initialCapacityOrElements; this.capacity = Math.max(16, elements.length * 2); this.buffer = new Array(this.capacity); this.head = 0; this.tail = elements.length; this.count = elements.length; for (let i = 0; i < elements.length; i++) { this.buffer[i] = elements[i]; } } else { // Initialize with capacity const initialCapacity = initialCapacityOrElements ?? 16; if (initialCapacity < 1) { throw new Error("Initial capacity must be at least 1"); } this.capacity = Math.max(initialCapacity, 16); this.buffer = new Array(this.capacity); this.head = 0; this.tail = 0; this.count = 0; } } /** * Returns the number of elements in the deque. * * @returns The number of elements * @timeComplexity O(1) */ get size() { return this.count; } /** * Checks if the deque is empty. * * @returns True if empty, false otherwise * @timeComplexity O(1) */ isEmpty() { return this.count === 0; } /** * Removes all elements from the deque. * * @timeComplexity O(1) */ clear() { this.buffer = new Array(16); this.head = 0; this.tail = 0; this.count = 0; this.capacity = 16; } /** * Doubles the internal buffer capacity when full. * * @private * @timeComplexity O(n) - amortized O(1) per operation */ grow() { const newCapacity = this.capacity * 2; const newBuffer = new Array(newCapacity); // Copy elements to new buffer starting at index 0 for (let i = 0; i < this.count; i++) { newBuffer[i] = this.buffer[(this.head + i) % this.capacity]; } this.buffer = newBuffer; this.head = 0; this.tail = this.count; this.capacity = newCapacity; } /** * Adds an element to the front of the deque. * Alias: pushFront, unshift * * @param value - The value to add * @timeComplexity O(1) amortized * @spaceComplexity O(1) amortized * * @example * ```typescript * deque.pushFront(1); // [1] * deque.pushFront(2); // [2, 1] * ``` */ pushFront(value) { if (this.count === this.capacity) { this.grow(); } this.head = (this.head - 1 + this.capacity) % this.capacity; this.buffer[this.head] = value; this.count++; } /** * Adds an element to the back of the deque. * Alias: pushBack, push, enqueue * * @param value - The value to add * @timeComplexity O(1) amortized * @spaceComplexity O(1) amortized * * @example * ```typescript * deque.pushBack(1); // [1] * deque.pushBack(2); // [1, 2] * ``` */ pushBack(value) { if (this.count === this.capacity) { this.grow(); } this.buffer[this.tail] = value; this.tail = (this.tail + 1) % this.capacity; this.count++; } /** * Removes and returns the element at the front of the deque. * Alias: popFront, shift, dequeue * * @returns The front element, or undefined if empty * @timeComplexity O(1) * * @example * ```typescript * deque.pushBack(1); * deque.pushBack(2); * deque.popFront(); // 1 * ``` */ popFront() { if (this.count === 0) { return undefined; } const value = this.buffer[this.head]; this.buffer[this.head] = undefined; // Help GC this.head = (this.head + 1) % this.capacity; this.count--; return value; } /** * Removes and returns the element at the back of the deque. * Alias: popBack, pop * * @returns The back element, or undefined if empty * @timeComplexity O(1) * * @example * ```typescript * deque.pushBack(1); * deque.pushBack(2); * deque.popBack(); // 2 * ``` */ popBack() { if (this.count === 0) { return undefined; } this.tail = (this.tail - 1 + this.capacity) % this.capacity; const value = this.buffer[this.tail]; this.buffer[this.tail] = undefined; // Help GC this.count--; return value; } /** * Returns the front element without removing it. * Alias: peekFront, front * * @returns The front element, or undefined if empty * @timeComplexity O(1) * * @example * ```typescript * deque.pushBack(1); * deque.peekFront(); // 1 * deque.size; // 1 (not removed) * ``` */ peekFront() { if (this.count === 0) { return undefined; } return this.buffer[this.head]; } /** * Returns the back element without removing it. * Alias: peekBack, back * * @returns The back element, or undefined if empty * @timeComplexity O(1) * * @example * ```typescript * deque.pushBack(1); * deque.pushBack(2); * deque.peekBack(); // 2 * ``` */ peekBack() { if (this.count === 0) { return undefined; } const backIndex = (this.tail - 1 + this.capacity) % this.capacity; return this.buffer[backIndex]; } /** * Returns the element at the specified index. * Index 0 is the front, index size-1 is the back. * * @param index - The index to access * @returns The element at the index, or undefined if out of bounds * @timeComplexity O(1) * * @example * ```typescript * deque.pushBack(1); * deque.pushBack(2); * deque.pushBack(3); * deque.at(1); // 2 * ``` */ at(index) { if (index < 0 || index >= this.count) { return undefined; } const actualIndex = (this.head + index) % this.capacity; return this.buffer[actualIndex]; } /** * Converts the deque to an array (front to back order). * * @returns Array representation of the deque * @timeComplexity O(n) * @spaceComplexity O(n) * * @example * ```typescript * deque.pushBack(1); * deque.pushBack(2); * deque.toArray(); // [1, 2] * ``` */ toArray() { const result = []; for (let i = 0; i < this.count; i++) { const index = (this.head + i) % this.capacity; result.push(this.buffer[index]); } return result; } /** * Creates an iterator for the deque (front to back). * * @returns Iterator for the deque * @timeComplexity O(1) per iteration, O(n) total * * @example * ```typescript * for (const value of deque) { * console.log(value); * } * ``` */ *[Symbol.iterator]() { for (let i = 0; i < this.count; i++) { const index = (this.head + i) % this.capacity; yield this.buffer[index]; } } // ============ Stack Aliases ============ /** * Stack operation: Push element to the back. * @see pushBack */ push(value) { this.pushBack(value); } /** * Stack operation: Pop element from the back. * @see popBack */ pop() { return this.popBack(); } /** * Stack operation: Peek at the back element. * @see peekBack */ peek() { return this.peekBack(); } /** * Stack operation: Alias for peek. * @see peekBack */ top() { return this.peekBack(); } // ============ Queue Aliases ============ /** * Queue operation: Add element to the back. * @see pushBack */ enqueue(value) { this.pushBack(value); } /** * Queue operation: Remove element from the front. * @see popFront */ dequeue() { return this.popFront(); } /** * Queue operation: Peek at the front element. * @see peekFront */ front() { return this.peekFront(); } /** * Queue operation: Peek at the back element. * @see peekBack */ back() { return this.peekBack(); } // ============ Array-like Aliases ============ /** * Array operation: Add element to the front. * @see pushFront */ unshift(value) { this.pushFront(value); } /** * Array operation: Remove element from the front. * @see popFront */ shift() { return this.popFront(); } /** * Returns a string representation of the deque. * * @returns String representation * @timeComplexity O(n) */ toString() { return `Deque[${this.toArray().join(", ")}]`; } /** * Creates a shallow copy of the deque. * * @returns A new deque with the same elements * @timeComplexity O(n) * @spaceComplexity O(n) * * @example * ```typescript * const deque1 = new Deque<number>(); * deque1.push(1); * const deque2 = deque1.clone(); * ``` */ clone() { const newDeque = new Deque(this.capacity); newDeque.buffer = [...this.buffer]; newDeque.head = this.head; newDeque.tail = this.tail; newDeque.count = this.count; newDeque.capacity = this.capacity; return newDeque; } } export { Deque }; export default Deque;