UNPKG

doubly-linked-list-typed

Version:
285 lines (284 loc) 12.3 kB
/** * data-structure-typed * * @author Pablo Zeng * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com> * @license MIT License */ import type { ElementCallback, StackOptions } from '../../types'; import { IterableElementBase } from '../base'; /** * 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> { constructor(elements?: Iterable<E> | Iterable<R>, options?: StackOptions<E, R>); protected _elements: E[]; /** * The elements function returns the elements of this set. * @return An array of elements */ get elements(): E[]; /** * The size() function returns the number of elements in an array. * @returns The size of the elements array. */ get size(): number; /** * Time Complexity: O(n) * Space Complexity: O(n) * * The function "fromArray" creates a new Stack object from an array of elements. * @param {E[]} elements - The `elements` parameter is an array of elements of type `E`. * @returns {Stack} The method is returning a new instance of the Stack class, initialized with the elements from the input * array. */ static fromArray<E>(elements: E[]): Stack<E>; /** * Time Complexity: O(1) * Space Complexity: O(1) * * The function checks if an array is empty and returns a boolean value. * @returns A boolean value indicating whether the `_elements` array is empty or not. */ isEmpty(): boolean; /** * Time Complexity: O(1) * Space Complexity: O(1) * * The `peek` function returns the last element of an array, or undefined if the array is empty. * @returns The `peek()` function returns the last element of the `_elements` array, or `undefined` if the array is empty. */ peek(): E | undefined; /** * Time Complexity: O(1) * Space Complexity: O(1) * * The push function adds an element to the stack and returns the updated stack. * @param {E} element - The parameter "element" is of type E, which means it can be any data type. * @returns The `push` method is returning the updated `Stack<E>` object. */ push(element: E): boolean; /** * Time Complexity: O(1) * Space Complexity: O(1) * * The `pop` function removes and returns the last element from an array, or returns undefined if the array is empty. * @returns The `pop()` method is returning the last element of the array `_elements` if the array is not empty. If the * array is empty, it returns `undefined`. */ pop(): E | undefined; /** * Time Complexity: O(k) * Space Complexity: O(1) * * The function `pushMany` iterates over elements and pushes them into an array after applying a * transformation function if provided. * @param {Iterable<E> | Iterable<R>} elements - The `elements` parameter in the `pushMany` function * is an iterable containing elements of type `E` or `R`. The function iterates over each element in * the iterable and pushes it into the data structure. If a transformation function `toElementFn` is * provided, it is used to * @returns The `pushMany` function is returning an array of boolean values indicating whether each * element was successfully pushed into the data structure. */ pushMany(elements: Iterable<E> | Iterable<R>): boolean[]; /** * Time Complexity: O(n) * Space Complexity: O(1) * * The toArray function returns a copy of the elements in an array. * @returns An array of type E. */ delete(element: E): boolean; /** * Time Complexity: O(n) * Space Complexity: O(1) * * The toArray function returns a copy of the elements in an array. * @returns An array of type E. */ deleteAt(index: number): boolean; /** * Time Complexity: O(1) * Space Complexity: O(1) * * The clear function clears the elements array. */ clear(): void; /** * Time Complexity: O(n) * Space Complexity: O(n) * * The `clone()` function returns a new `Stack` object with the same elements as the original stack. * @returns The `clone()` method is returning a new `Stack` object with a copy of the `_elements` array. */ clone(): Stack<E, R>; /** * Time Complexity: O(n) * Space Complexity: O(n) * * The `filter` function creates a new stack containing elements from the original stack that satisfy * a given predicate function. * @param predicate - The `predicate` parameter is a callback function that takes three arguments: * the current element being iterated over, the index of the current element, and the stack itself. * It should return a boolean value indicating whether the element should be included in the filtered * stack or not. * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value * to be used as `this` when executing the `predicate` function. If `thisArg` is provided, it will be * passed as the `this` value to the `predicate` function. If `thisArg` is * @returns The `filter` method is returning a new `Stack` object that contains the elements that * satisfy the given predicate function. */ filter(predicate: ElementCallback<E, R, boolean>, thisArg?: any): Stack<E, R>; /** * Time Complexity: O(n) * Space Complexity: O(n) * * The `map` function takes a callback function and applies it to each element in the stack, * returning a new stack with the results. * @param callback - The callback parameter is a function that will be called for each element in the * stack. It takes three arguments: the current element, the index of the element, and the stack * itself. It should return a new value that will be added to the new stack. * @param [toElementFn] - The `toElementFn` parameter is an optional function that can be used to * transform the raw element (`RM`) into a new element (`EM`) before pushing it into the new stack. * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that allows you to * specify the value of `this` within the callback function. It is used to set the context or scope * in which the callback function will be executed. If `thisArg` is provided, it will be used as the * value of * @returns a new Stack object with elements of type EM and raw elements of type RM. */ map<EM, RM>(callback: ElementCallback<E, R, EM>, toElementFn?: (rawElement: RM) => EM, thisArg?: any): Stack<EM, RM>; /** * Time Complexity: O(n) * Space Complexity: O(n) * * Custom iterator for the Stack class. * @returns An iterator object. */ protected _getIterator(): IterableIterator<E>; }