UNPKG

deque-typed

Version:
460 lines (459 loc) 17.3 kB
/** * data-structure-typed * * @author Pablo Zeng * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com> * @license MIT License */ import type { DequeOptions, ElementCallback, IterableElementBaseOptions, IterableWithSizeOrLength, LinearBaseOptions } from '../../types'; import { LinearBase } from '../base/linear-base'; /** * Deque implemented with circular buckets allowing O(1) amortized push/pop at both ends. * @remarks Time O(1), Space O(1) * @template E * @template R * 1. Operations at Both Ends: Supports adding and removing elements at both the front and back of the queue. This allows it to be used as a stack (last in, first out) and a queue (first in, first out). * 2. Efficient Random Access: Being based on an array, it offers fast random access capability, allowing constant time access to any element. * 3. Continuous Memory Allocation: Since it is based on an array, all elements are stored contiguously in memory, which can bring cache friendliness and efficient memory access. * 4. Efficiency: Adding and removing elements at both ends of a deque is usually very fast. However, when the dynamic array needs to expand, it may involve copying the entire array to a larger one, and this operation has a time complexity of O(n). * 5. Performance jitter: Deque may experience performance jitter, but DoublyLinkedList will not * @example * // basic Deque creation and push/pop operations * // Create a simple Deque with initial values * const deque = new Deque([1, 2, 3, 4, 5]); * * // Verify the deque maintains insertion order * console.log([...deque]); // [1, 2, 3, 4, 5]; * * // Check length * console.log(deque.length); // 5; * * // Push to the end * deque.push(6); * console.log(deque.length); // 6; * * // Pop from the end * const last = deque.pop(); * console.log(last); // 6; * @example * // Deque shift and unshift operations * const deque = new Deque<number>([20, 30, 40]); * * // Unshift adds to the front * deque.unshift(10); * console.log([...deque]); // [10, 20, 30, 40]; * * // Shift removes from the front (O(1) complexity!) * const first = deque.shift(); * console.log(first); // 10; * * // Verify remaining elements * console.log([...deque]); // [20, 30, 40]; * console.log(deque.length); // 3; * @example * // Deque peek at both ends * const deque = new Deque<number>([10, 20, 30, 40, 50]); * * // Get first element without removing * const first = deque.at(0); * console.log(first); // 10; * * // Get last element without removing * const last = deque.at(deque.length - 1); * console.log(last); // 50; * * // Length unchanged * console.log(deque.length); // 5; * @example * // Deque for...of iteration and reverse * const deque = new Deque<string>(['A', 'B', 'C', 'D']); * * // Iterate forward * const forward: string[] = []; * for (const item of deque) { * forward.push(item); * } * console.log(forward); // ['A', 'B', 'C', 'D']; * * // Reverse the deque * deque.reverse(); * const backward: string[] = []; * for (const item of deque) { * backward.push(item); * } * console.log(backward); // ['D', 'C', 'B', 'A']; * @example * // Deque as sliding window for stream processing * interface DataPoint { * timestamp: number; * value: number; * sensor: string; * } * * // Create a deque-based sliding window for real-time data aggregation * const windowSize = 3; * const dataWindow = new Deque<DataPoint>(); * * // Simulate incoming sensor data stream * const incomingData: DataPoint[] = [ * { timestamp: 1000, value: 25.5, sensor: 'temp-01' }, * { timestamp: 1100, value: 26.2, sensor: 'temp-01' }, * { timestamp: 1200, value: 25.8, sensor: 'temp-01' }, * { timestamp: 1300, value: 27.1, sensor: 'temp-01' }, * { timestamp: 1400, value: 26.9, sensor: 'temp-01' } * ]; * * const windowResults: Array<{ avgValue: number; windowSize: number }> = []; * * for (const dataPoint of incomingData) { * // Add new data to the end * dataWindow.push(dataPoint); * * // Remove oldest data when window exceeds size (O(1) from front) * if (dataWindow.length > windowSize) { * dataWindow.shift(); * } * * // Calculate average of current window * let sum = 0; * for (const point of dataWindow) { * sum += point.value; * } * const avg = sum / dataWindow.length; * * windowResults.push({ * avgValue: Math.round(avg * 10) / 10, * windowSize: dataWindow.length * }); * } * * // Verify sliding window behavior * console.log(windowResults.length); // 5; * console.log(windowResults[0].windowSize); // 1; // First window has 1 element * console.log(windowResults[2].windowSize); // 3; // Windows are at max size from 3rd onwards * console.log(windowResults[4].windowSize); // 3; // Last window still has 3 elements * console.log(dataWindow.length); // 3; */ export declare class Deque<E = any, R = any> extends LinearBase<E, R> { protected _equals: (a: E, b: E) => boolean; /** * Create a Deque and optionally bulk-insert elements. * @remarks Time O(N), Space O(N) * @param [elements] - Iterable (or iterable-like) of elements/records to insert. * @param [options] - Options such as bucketSize, toElementFn, and maxLen. * @returns New Deque instance. */ constructor(elements?: IterableWithSizeOrLength<E> | IterableWithSizeOrLength<R>, options?: DequeOptions<E, R>); protected _bucketSize: number; /** * Get the current bucket size. * @remarks Time O(1), Space O(1) * @returns Bucket capacity per bucket. */ get bucketSize(): number; protected _bucketFirst: number; /** * Get the index of the first bucket in use. * @remarks Time O(1), Space O(1) * @returns Zero-based bucket index. */ get bucketFirst(): number; protected _firstInBucket: number; /** * Get the index inside the first bucket. * @remarks Time O(1), Space O(1) * @returns Zero-based index within the first bucket. */ get firstInBucket(): number; protected _bucketLast: number; /** * Get the index of the last bucket in use. * @remarks Time O(1), Space O(1) * @returns Zero-based bucket index. */ get bucketLast(): number; protected _lastInBucket: number; /** * Get the index inside the last bucket. * @remarks Time O(1), Space O(1) * @returns Zero-based index within the last bucket. */ get lastInBucket(): number; protected _bucketCount: number; /** * Get the number of buckets allocated. * @remarks Time O(1), Space O(1) * @returns Bucket count. */ get bucketCount(): number; protected _buckets: E[][]; /** * Get the internal buckets array. * @remarks Time O(1), Space O(1) * @returns Array of buckets storing values. */ get buckets(): E[][]; protected _length: number; /** * Get the number of elements in the deque. * @remarks Time O(1), Space O(1) * @returns Current length. */ get length(): number; /** * Get the first element without removing it. * @remarks Time O(1), Space O(1) * @returns First element or undefined. */ get first(): E | undefined; /** * Get the last element without removing it. * @remarks Time O(1), Space O(1) * @returns Last element or undefined. */ get last(): E | undefined; /** * Create a Deque from an array of elements. * @remarks Time O(N), Space O(N) * @template E * @template R * @param this - Constructor (subclass) to instantiate. * @param data - Array of elements to insert in order. * @param [options] - Options forwarded to the constructor. * @returns A new Deque populated from the array. */ static fromArray<E, R = any>(this: new (elements?: IterableWithSizeOrLength<E> | IterableWithSizeOrLength<R>, options?: DequeOptions<E, R>) => any, data: E[], options?: DequeOptions<E, R>): any; /** * Append one element at the back. * @remarks Time O(1) amortized, Space O(1) * @param element - Element to append. * @returns True when appended. */ push(element: E): boolean; /** * Remove and return the last element. * @remarks Time O(1), Space O(1) * @returns Removed element or undefined. */ pop(): E | undefined; /** * Remove and return the first element. * @remarks Time O(1) amortized, Space O(1) * @returns Removed element or undefined. */ shift(): E | undefined; /** * Prepend one element at the front. * @remarks Time O(1) amortized, Space O(1) * @param element - Element to prepend. * @returns True when prepended. */ unshift(element: E): boolean; /** * Append a sequence of elements. * @remarks Time O(N), Space O(1) * @param elements - Iterable (or iterable-like) of elements/records. * @returns Array of per-element success flags. */ pushMany(elements: IterableWithSizeOrLength<E> | IterableWithSizeOrLength<R>): boolean[]; /** * Prepend a sequence of elements. * @remarks Time O(N), Space O(1) * @param [elements] - Iterable (or iterable-like) of elements/records. * @returns Array of per-element success flags. */ unshiftMany(elements?: IterableWithSizeOrLength<E> | IterableWithSizeOrLength<R>): boolean[]; /** * Check whether the deque is empty. * @remarks Time O(1), Space O(1) * @returns True if length is 0. */ isEmpty(): boolean; /** * Remove all elements and reset structure. * @remarks Time O(1), Space O(1) * @returns void */ clear(): void; /** * Get the element at a given position. * @remarks Time O(1), Space O(1) * @param pos - Zero-based position from the front. * @returns Element or undefined. */ at(pos: number): E | undefined; /** * Replace the element at a given position. * @remarks Time O(1), Space O(1) * @param pos - Zero-based position from the front. * @param element - New element value. * @returns True if updated. */ setAt(pos: number, element: E): boolean; /** * Insert repeated copies of an element at a position. * @remarks Time O(N), Space O(1) * @param pos - Zero-based position from the front. * @param element - Element to insert. * @param [num] - Number of times to insert (default 1). * @returns True if inserted. */ addAt(pos: number, element: E, num?: number): boolean; /** * Cut the deque to keep items up to index; optionally mutate in-place. * @remarks Time O(N), Space O(1) * @param pos - Last index to keep. * @param [isCutSelf] - When true, mutate this deque; otherwise return a new deque. * @returns This deque if in-place; otherwise a new deque of the prefix. */ cut(pos: number, isCutSelf?: boolean): Deque<E>; /** * Remove and/or insert elements at a position (array-like behavior). * @remarks Time O(N + M), Space O(M) * @param start - Start index (clamped to [0, length]). * @param [deleteCount] - Number of elements to remove (default: length - start). * @param [items] - Elements to insert after `start`. * @returns A new deque containing the removed elements (typed as `this`). */ splice(start: number, deleteCount?: number, ...items: E[]): this; /** * Cut the deque to keep items from index onward; optionally mutate in-place. * @remarks Time O(N), Space O(1) * @param pos - First index to keep. * @param [isCutSelf] - When true, mutate this deque; otherwise return a new deque. * @returns This deque if in-place; otherwise a new deque of the suffix. */ cutRest(pos: number, isCutSelf?: boolean): Deque<E>; /** * Delete the element at a given position. * @remarks Time O(N), Space O(1) * @param pos - Zero-based position from the front. * @returns Removed element or undefined. */ deleteAt(pos: number): E | undefined; /** * Delete the first occurrence of a value. * @remarks Time O(N), Space O(1) * @param element - Element to remove (using the configured equality). * @returns True if an element was removed. */ delete(element: E): boolean; /** * Delete the first element matching a predicate. * @remarks Time O(N), Space O(1) * @param predicate - Function (value, index, deque) → boolean. * @returns True if a match was removed. */ deleteWhere(predicate: (value: E, index: number, deque: this) => boolean): boolean; /** * Set the equality comparator used by delete operations. * @remarks Time O(1), Space O(1) * @param equals - Equality predicate (a, b) → boolean. * @returns This deque. */ setEquality(equals: (a: E, b: E) => boolean): this; /** * Reverse the deque by reversing buckets and pointers. * @remarks Time O(N), Space O(N) * @returns This deque. */ reverse(): this; /** * Deduplicate consecutive equal elements in-place. * @remarks Time O(N), Space O(1) * @returns This deque. */ unique(): this; /** * Trim unused buckets to fit exactly the active range. * @remarks Time O(N), Space O(1) * @returns void */ shrinkToFit(): void; /** * Deep clone this deque, preserving options. * @remarks Time O(N), Space O(N) * @returns A new deque with the same content and options. */ clone(): this; /** * Filter elements into a new deque of the same class. * @remarks Time O(N), Space O(N) * @param predicate - Predicate (value, index, deque) → boolean to keep element. * @param [thisArg] - Value for `this` inside the predicate. * @returns A new deque with kept elements. */ filter(predicate: ElementCallback<E, R, boolean>, thisArg?: any): this; /** * Map elements into a new deque of the same element type. * @remarks Time O(N), Space O(N) * @param callback - Mapping function (value, index, deque) → newValue. * @param [thisArg] - Value for `this` inside the callback. * @returns A new deque with mapped values. */ mapSame(callback: ElementCallback<E, R, E>, thisArg?: any): this; /** * Map elements into a new deque (possibly different element type). * @remarks Time O(N), Space O(N) * @template EM * @template RM * @param callback - Mapping function (value, index, deque) → newElement. * @param [options] - Options for the output deque (e.g., bucketSize, toElementFn, maxLen). * @param [thisArg] - Value for `this` inside the callback. * @returns A new Deque with mapped elements. */ map<EM, RM>(callback: ElementCallback<E, R, EM>, options?: IterableElementBaseOptions<EM, RM>, thisArg?: any): Deque<EM, RM>; /** * (Protected) Set the internal bucket size. * @remarks Time O(1), Space O(1) * @param size - Bucket capacity to assign. * @returns void */ protected _setBucketSize(size: number): void; /** * (Protected) Iterate elements from front to back. * @remarks Time O(N), Space O(1) * @returns Iterator of elements. */ protected _getIterator(): IterableIterator<E>; /** * (Protected) Reallocate buckets to make room near the ends. * @remarks Time O(N), Space O(N) * @param [needBucketNum] - How many extra buckets to add; defaults to half of current. * @returns void */ protected _reallocate(needBucketNum?: number): void; /** * (Protected) Translate a logical position to bucket/offset. * @remarks Time O(1), Space O(1) * @param pos - Zero-based position. * @returns An object containing bucketIndex and indexInBucket. */ protected _getBucketAndPosition(pos: number): { bucketIndex: number; indexInBucket: number; }; /** * (Protected) Create an empty instance of the same concrete class. * @remarks Time O(1), Space O(1) * @param [options] - Options forwarded to the constructor. * @returns An empty like-kind deque instance. */ protected _createInstance(options?: LinearBaseOptions<E, R>): this; /** * (Protected) Create a like-kind deque seeded by elements. * @remarks Time O(N), Space O(N) * @template T * @template RR * @param [elements] - Iterable used to seed the new deque. * @param [options] - Options forwarded to the constructor. * @returns A like-kind Deque instance. */ protected _createLike<T = E, RR = R>(elements?: IterableWithSizeOrLength<T> | IterableWithSizeOrLength<RR>, options?: DequeOptions<T, RR>): any; /** * (Protected) Iterate elements from back to front. * @remarks Time O(N), Space O(1) * @returns Iterator of elements. */ protected _getReverseIterator(): IterableIterator<E>; }