UNPKG

tree-multimap-typed

Version:
307 lines (306 loc) 12.2 kB
/** * data-structure-typed * * @author Pablo Zeng * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com> * @license MIT License */ import type { ElementCallback, IterableElementBaseOptions, StackOptions } from '../../types'; import { IterableElementBase } from '../base'; /** * LIFO stack with array storage and optional record→element conversion. * @remarks Time O(1), Space O(1) * @template E * @template R * 1. Last In, First Out (LIFO): The core characteristic of a stack is its last in, first out nature, meaning the last element added to the stack will be the first to be removed. * 2. Uses: Stacks are commonly used for managing a series of tasks or elements that need to be processed in a last in, first out manner. They are widely used in various scenarios, such as in function calls in programming languages, evaluation of arithmetic expressions, and backtracking algorithms. * 3. Performance: Stack operations are typically O(1) in time complexity, meaning that regardless of the stack's size, adding, removing, and viewing the top element are very fast operations. * 4. Function Calls: In most modern programming languages, the records of function calls are managed through a stack. When a function is called, its record (including parameters, local variables, and return address) is 'pushed' into the stack. When the function returns, its record is 'popped' from the stack. * 5. Expression Evaluation: Used for the evaluation of arithmetic or logical expressions, especially when dealing with parenthesis matching and operator precedence. * 6. Backtracking Algorithms: In problems where multiple branches need to be explored but only one branch can be explored at a time, stacks can be used to save the state at each branching point. * @example * // Balanced Parentheses or Brackets * type ValidCharacters = ')' | '(' | ']' | '[' | '}' | '{'; * * const stack = new Stack<string>(); * const input: ValidCharacters[] = '[({})]'.split('') as ValidCharacters[]; * const matches: { [key in ValidCharacters]?: ValidCharacters } = { ')': '(', ']': '[', '}': '{' }; * for (const char of input) { * if ('([{'.includes(char)) { * stack.push(char); * } else if (')]}'.includes(char)) { * if (stack.pop() !== matches[char]) { * fail('Parentheses are not balanced'); * } * } * } * console.log(stack.isEmpty()); // true * @example * // Expression Evaluation and Conversion * const stack = new Stack<number>(); * const expression = [5, 3, '+']; // Equivalent to 5 + 3 * expression.forEach(token => { * if (typeof token === 'number') { * stack.push(token); * } else { * const b = stack.pop()!; * const a = stack.pop()!; * stack.push(token === '+' ? a + b : 0); // Only handling '+' here * } * }); * console.log(stack.pop()); // 8 * @example * // Depth-First Search (DFS) * const stack = new Stack<number>(); * const graph: { [key in number]: number[] } = { 1: [2, 3], 2: [4], 3: [5], 4: [], 5: [] }; * const visited: number[] = []; * stack.push(1); * while (!stack.isEmpty()) { * const node = stack.pop()!; * if (!visited.includes(node)) { * visited.push(node); * graph[node].forEach(neighbor => stack.push(neighbor)); * } * } * console.log(visited); // [1, 3, 5, 2, 4] * @example * // Backtracking Algorithms * const stack = new Stack<[number, number]>(); * const maze = [ * ['S', ' ', 'X'], * ['X', ' ', 'X'], * [' ', ' ', 'E'] * ]; * const start: [number, number] = [0, 0]; * const end = [2, 2]; * const directions = [ * [0, 1], // To the right * [1, 0], // down * [0, -1], // left * [-1, 0] // up * ]; * * const visited = new Set<string>(); // Used to record visited nodes * stack.push(start); * const path: number[][] = []; * * while (!stack.isEmpty()) { * const [x, y] = stack.pop()!; * if (visited.has(`${x},${y}`)) continue; // Skip already visited nodes * visited.add(`${x},${y}`); * * path.push([x, y]); * * if (x === end[0] && y === end[1]) { * break; // Find the end point and exit * } * * for (const [dx, dy] of directions) { * const nx = x + dx; * const ny = y + dy; * if ( * maze[nx]?.[ny] === ' ' || // feasible path * maze[nx]?.[ny] === 'E' // destination * ) { * stack.push([nx, ny]); * } * } * } * * expect(path).toContainEqual(end); * @example * // Function Call Stack * const functionStack = new Stack<string>(); * functionStack.push('main'); * functionStack.push('foo'); * functionStack.push('bar'); * console.log(functionStack.pop()); // 'bar' * console.log(functionStack.pop()); // 'foo' * console.log(functionStack.pop()); // 'main' * @example * // Simplify File Paths * const stack = new Stack<string>(); * const path = '/a/./b/../../c'; * path.split('/').forEach(segment => { * if (segment === '..') stack.pop(); * else if (segment && segment !== '.') stack.push(segment); * }); * console.log(stack.elements.join('/')); // 'c' * @example * // Stock Span Problem * const stack = new Stack<number>(); * const prices = [100, 80, 60, 70, 60, 75, 85]; * const spans: number[] = []; * prices.forEach((price, i) => { * while (!stack.isEmpty() && prices[stack.peek()!] <= price) { * stack.pop(); * } * spans.push(stack.isEmpty() ? i + 1 : i - stack.peek()!); * stack.push(i); * }); * console.log(spans); // [1, 1, 1, 2, 1, 4, 6] */ export declare class Stack<E = any, R = any> extends IterableElementBase<E, R> { protected _equals: (a: E, b: E) => boolean; /** * Create a Stack and optionally bulk-push elements. * @remarks Time O(N), Space O(N) * @param [elements] - Iterable of elements (or raw records if toElementFn is set). * @param [options] - Options such as toElementFn and equality function. * @returns New Stack instance. */ constructor(elements?: Iterable<E> | Iterable<R>, options?: StackOptions<E, R>); protected _elements: E[]; /** * Get the backing array of elements. * @remarks Time O(1), Space O(1) * @returns Internal elements array. */ get elements(): E[]; /** * Get the number of stored elements. * @remarks Time O(1), Space O(1) * @returns Current size. */ get size(): number; /** * Create a stack from an array of elements. * @remarks Time O(N), Space O(N) * @template E * @template R * @param this - The constructor (subclass) to instantiate. * @param elements - Array of elements to push in order. * @param [options] - Options forwarded to the constructor. * @returns A new Stack populated from the array. */ static fromArray<E, R = any>(this: new (elements?: Iterable<E> | Iterable<R>, options?: StackOptions<E, R>) => any, elements: E[], options?: StackOptions<E, R>): any; /** * Check whether the stack is empty. * @remarks Time O(1), Space O(1) * @returns True if size is 0. */ isEmpty(): boolean; /** * Get the top element without removing it. * @remarks Time O(1), Space O(1) * @returns Top element or undefined. */ peek(): E | undefined; /** * Push one element onto the top. * @remarks Time O(1), Space O(1) * @param element - Element to push. * @returns True when pushed. */ push(element: E): boolean; /** * Pop and return the top element. * @remarks Time O(1), Space O(1) * @returns Removed element or undefined. */ pop(): E | undefined; /** * Push many elements from an iterable. * @remarks Time O(N), Space O(1) * @param elements - Iterable of elements (or raw records if toElementFn is set). * @returns Array of per-element success flags. */ pushMany(elements: Iterable<E> | Iterable<R>): boolean[]; /** * Delete the first occurrence of a specific element. * @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 element at an index. * @remarks Time O(N), Space O(1) * @param index - Zero-based index from the bottom. * @returns True if removed. */ deleteAt(index: number): boolean; /** * Delete the first element that satisfies a predicate. * @remarks Time O(N), Space O(1) * @param predicate - Function (value, index, stack) → boolean to decide deletion. * @returns True if a match was removed. */ deleteWhere(predicate: (value: E, index: number, stack: this) => boolean): boolean; /** * Remove all elements and reset storage. * @remarks Time O(1), Space O(1) * @returns void */ clear(): void; /** * Deep clone this stack. * @remarks Time O(N), Space O(N) * @returns A new stack with the same content. */ clone(): this; /** * Filter elements into a new stack of the same class. * @remarks Time O(N), Space O(N) * @param predicate - Predicate (value, index, stack) → boolean to keep value. * @param [thisArg] - Value for `this` inside the predicate. * @returns A new stack with kept values. */ filter(predicate: ElementCallback<E, R, boolean>, thisArg?: unknown): this; /** * Map values into a new stack of the same element type. * @remarks Time O(N), Space O(N) * @param callback - Mapping function (value, index, stack) → newValue. * @param [thisArg] - Value for `this` inside the callback. * @returns A new stack with mapped values. */ mapSame(callback: ElementCallback<E, R, E>, thisArg?: unknown): this; /** * Map values into a new stack (possibly different element type). * @remarks Time O(N), Space O(N) * @template EM * @template RM * @param callback - Mapping function (value, index, stack) → newElement. * @param [options] - Options for the output stack (e.g., toElementFn). * @param [thisArg] - Value for `this` inside the callback. * @returns A new Stack with mapped elements. */ map<EM, RM>(callback: ElementCallback<E, R, EM>, options?: IterableElementBaseOptions<EM, RM>, thisArg?: unknown): Stack<EM, RM>; /** * Set the equality comparator used by delete/search operations. * @remarks Time O(1), Space O(1) * @param equals - Equality predicate (a, b) → boolean. * @returns This stack. */ setEquality(equals: (a: E, b: E) => boolean): this; /** * (Protected) Find the index of a target element using the equality function. * @remarks Time O(N), Space O(1) * @param target - Element to search for. * @returns Index or -1 if not found. */ protected _indexOfByEquals(target: E): 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 stack instance. */ protected _createInstance(options?: StackOptions<E, R>): this; /** * (Protected) Create a like-kind stack and seed it from an iterable. * @remarks Time O(N), Space O(N) * @template T * @template RR * @param [elements] - Iterable used to seed the new stack. * @param [options] - Options forwarded to the constructor. * @returns A like-kind Stack instance. */ protected _createLike<T = E, RR = R>(elements?: Iterable<T> | Iterable<RR>, options?: StackOptions<T, RR>): Stack<T, RR>; /** * (Protected) Iterate elements from bottom to top. * @remarks Time O(N), Space O(1) * @returns Iterator of elements. */ protected _getIterator(): IterableIterator<E>; }