deque-typed
Version:
460 lines (459 loc) • 17.3 kB
TypeScript
/**
* 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>;
}