diff --git a/data_structures/binary_heap.ts b/data_structures/binary_heap.ts index 0ab97a869544..b4351532ddee 100644 --- a/data_structures/binary_heap.ts +++ b/data_structures/binary_heap.ts @@ -56,25 +56,119 @@ function getParentIndex(index: number) { * assertEquals([...words], ["truck", "tank", "car"]); * assertEquals([...words], []); * ``` + * + * @typeparam T The type of the values stored in the binary heap. */ export class BinaryHeap implements Iterable { #data: T[] = []; + + /** + * Construct an empty binary heap. + * + * @example Creating an empty binary heap + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = new BinaryHeap(); + * ``` + * + * @example Creating a binary heap with a custom comparison function + * ```ts + * import { BinaryHeap, ascend } from "@std/data-structures"; + * const heap = new BinaryHeap(ascend); + * ``` + * + * @param compare A custom comparison function to sort the values in the heap. By default, the values are sorted in descending order. + */ constructor(private compare: (a: T, b: T) => number = descend) {} - /** Returns the underlying cloned array in arbitrary order without sorting */ + + /** + * Returns the underlying cloned array in arbitrary order without sorting. + * + * @example Getting the underlying array + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 5, 2]); + * heap.toArray(); // [ 5, 4, 3, 1, 2 ] + * ``` + * + * @returns An array containing the values in the binary heap. + */ toArray(): T[] { return Array.from(this.#data); } - /** Creates a new binary heap from an array like or iterable object. */ - static from( - collection: ArrayLike | Iterable | BinaryHeap, - ): BinaryHeap; + + /** + * Creates a new binary heap from an array like, an iterable object, or an + * existing binary heap. + * + * A custom comparison function can be provided to sort the values in a + * specific order. By default, the values are sorted in descending order, + * unless a {@link BinaryHeap} is passed, in which case the comparison + * function is copied from the input heap. + * + * @example Creating a binary heap from an array like + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 5, 2]); + * ``` + * + * @example Creating a binary heap from an iterable object + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from((function*() { yield* [4, 1, 3, 5, 2]; })()); + * ``` + * + * @example Creating a binary heap from an existing binary heap + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 5, 2]); + * const copy = BinaryHeap.from(heap); + * ``` + * + * @example Creating a binary heap from an array like with a custom comparison function + * ```ts + * import { BinaryHeap, ascend } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 5, 2], { compare: ascend }); + * ``` + * + * @typeparam T The type of the values stored in the binary heap. + * @param collection An array like, an iterable object, or an existing binary heap. + * @param options An optional options object to customize the comparison function. + * @returns A new binary heap containing the values from the passed collection. + */ static from( collection: ArrayLike | Iterable | BinaryHeap, - options: { + options?: { compare?: (a: T, b: T) => number; }, ): BinaryHeap; - static from( + /** + * Creates a new binary heap from an array like, an iterable object, or an + * existing binary heap. + * + * A custom mapping function can be provided to transform the values before + * inserting them into the heap. + * + * A custom comparison function can be provided to sort the values in a + * specific order. By default, the values are sorted in descending order, + * unless a {@link BinaryHeap} is passed, in which case the comparison + * function is copied from the input heap. The comparison operator is used to + * sort the values in the heap after mapping the values. + * + * @example Creating a binary heap from an array like with a custom mapping function + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 5, 2], { map: (value) => value * 2 }); + * ``` + * + * @typeparam T The type of the values in the passed collection. + * @typeparam U The type of the values stored in the binary heap. + * @typeparam V The type of the `this` value when calling the mapping function. Defaults to `undefined`. + * @param collection An array like, an iterable object, or an existing binary heap. + * @param options The options object to customize the mapping and comparison functions. The `thisArg` property can be used to set the `this` value when calling the mapping function. + * @returns A new binary heap containing the mapped values from the passed collection. + */ + static from( collection: ArrayLike | Iterable | BinaryHeap, options: { compare?: (a: U, b: U) => number; @@ -114,17 +208,74 @@ export class BinaryHeap implements Iterable { return result; } - /** The amount of values stored in the binary heap. */ + /** + * The count of values stored in the binary heap. + * + * The complexity of this operation is O(1). + * + * @example Getting the length of the binary heap + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 5, 2]); + * heap.length; // 5 + * ``` + * + * @returns The count of values stored in the binary heap. + */ get length(): number { return this.#data.length; } - /** Returns the greatest value in the binary heap, or undefined if it is empty. */ + /** + * Get the greatest value from the binary heap without removing it, or + * undefined if the heap is empty. + * + * The complexity of this operation is O(1). + * + * @example Getting the greatest value from the binary heap + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 5, 2]); + * heap.peek(); // 5 + * ``` + * + * @example Getting the greatest value from an empty binary heap + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = new BinaryHeap(); + * heap.peek(); // undefined + * ``` + * + * @returns The greatest value from the binary heap, or undefined if it is empty. + */ peek(): T | undefined { return this.#data[0]; } - /** Removes the greatest value from the binary heap and returns it, or null if it is empty. */ + /** + * Remove the greatest value from the binary heap and return it, or return + * undefined if the heap is empty. + * + * @example Removing the greatest value from the binary heap + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 5, 2]); + * heap.pop(); // 5 + * [...heap]; // [ 4, 3, 2, 1 ] + * ``` + * + * The complexity of this operation is on average and worst case O(log n), + * where n is the count of values stored in the binary heap. + * + * @example Removing the greatest value from an empty binary heap + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = new BinaryHeap(); + * heap.pop(); // undefined + * ``` + * + * @returns The greatest value from the binary heap, or undefined if the heap is empty. + */ pop(): T | undefined { const size: number = this.#data.length - 1; swap(this.#data, 0, size); @@ -148,7 +299,24 @@ export class BinaryHeap implements Iterable { return this.#data.pop(); } - /** Adds values to the binary heap. */ + /** + * Add one or more values to the binary heap, returning the new length of the + * heap. + * + * The complexity of this operation is O(1) on average and O(log n) in the + * worst case, where n is the count of values stored in the binary heap. + * + * @example Adding values to the binary heap + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 2]); + * heap.push(5); + * [...heap]; // [ 5, 4, 3, 1, 2 ] + * ``` + * + * @param values The values to add to the binary heap. + * @returns The new length of the binary heap. + */ push(...values: T[]): number { for (const value of values) { let index: number = this.#data.length; @@ -165,23 +333,78 @@ export class BinaryHeap implements Iterable { return this.#data.length; } - /** Removes all values from the binary heap. */ + /** + * Remove all values from the binary heap. + * + * @example Clearing the binary heap + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 5, 2]); + * heap.clear(); + * [...heap]; // [] + * ``` + */ clear() { this.#data = []; } - /** Checks if the binary heap is empty. */ + /** + * Check if the binary heap is empty. + * + * @example Checking if the binary heap is empty + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = new BinaryHeap(); + * heap.isEmpty(); // true + * heap.push(42); + * heap.isEmpty(); // false + * ``` + * + * @returns true if the binary heap is empty, otherwise false. + */ isEmpty(): boolean { return this.#data.length === 0; } - /** Returns an iterator for retrieving and removing values from the binary heap. */ + /** + * Create an iterator that retrieves values from the binary heap in order + * from greatest to least. The binary heap is drained in the process. + * + * To avoid draining the binary heap, create a copy using + * {@link BinaryHeap.from} and then call {@link BinaryHeap#drain} on the copy. + * + * @example Draining the binary heap + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 5, 2]); + * [...heap.drain()]; // [ 5, 4, 3, 2, 1 ] + * [...heap.drain()]; // [] + * ``` + * + * @returns An iterator for retrieving and removing values from the binary heap. + */ *drain(): IterableIterator { while (!this.isEmpty()) { yield this.pop() as T; } } + /** + * Create an iterator that retrieves values from the binary heap in order + * from greatest to least. The binary heap is drained in the process. + * + * See {@link BinaryHeap#values}. + * + * @example Getting an iterator for the binary heap + * ```ts + * import { BinaryHeap } from "@std/data-structures"; + * const heap = BinaryHeap.from([4, 1, 3, 5, 2]); + * [...heap]; // [ 5, 4, 3, 2, 1 ] + * [...heap]; // [] + * ``` + * + * @returns An iterator for retrieving and removing values from the binary heap. + */ *[Symbol.iterator](): IterableIterator { yield* this.drain(); } diff --git a/data_structures/binary_search_tree.ts b/data_structures/binary_search_tree.ts index dde3994db61c..ba62c5343673 100644 --- a/data_structures/binary_search_tree.ts +++ b/data_structures/binary_search_tree.ts @@ -13,7 +13,7 @@ type Direction = "left" | "right"; * For performance, it's recommended that you use a self-balancing binary search * tree instead of this one unless you are extending this to create a * self-balancing tree. See RedBlackTree for an example of how BinarySearchTree - * can be extended to create a self-balancing binary search tree. + * can be extended to create a self-balancing binary search tree. * * | Method | Average Case | Worst Case | * | ------------- | ------------ | ---------- | @@ -86,25 +86,124 @@ type Direction = "left" | "right"; * "helicopter", * ]); * ``` + * + * @typeparam T The type of the values stored in the binary search tree. */ export class BinarySearchTree implements Iterable { protected root: BinarySearchNode | null = null; protected _size = 0; + + /** + * Construct an empty binary search tree. + * + * @example Creating an empty binary search tree + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = new BinarySearchTree(); + * ``` + * + * @example Creating a binary search tree with a custom comparison function + * ```ts + * import { BinarySearchTree, ascend } from "@std/data-structures"; + * + * const tree = new BinarySearchTree<{ price: number, name: string }>( + * (a, b) => ascend(a.price, b.price) || ascend(a.name, b.name) + * ); + * ``` + * + * To create a binary search tree from an array like, an iterable object, or an + * existing binary search tree, use the {@link BinarySearchTree.from} method. + * + * @param compare A custom comparison function to sort the values in the tree. By default, the values are sorted in ascending order. + */ constructor( protected compare: (a: T, b: T) => number = ascend, ) {} - /** Creates a new binary search tree from an array like or iterable object. */ - static from( - collection: ArrayLike | Iterable | BinarySearchTree, - ): BinarySearchTree; + /** + * Creates a new binary search tree from an array like, an iterable object, + * or an existing binary search tree. + * + * A custom comparison function can be provided to sort the values in a + * specific order. By default, the values are sorted in ascending order, + * unless a {@link BinarySearchTree} is passed, in which case the comparison + * function is copied from the input tree. + * + * @example Creating a binary search tree from an array like + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([42, 43, 41]); + * ``` + * + * @example Creating a binary search tree from an iterable object + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from((function*() { + * yield 42; + * yield 43; + * yield 41; + * })()); + * ``` + * + * @example Creating a binary search tree from an existing binary search tree + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([42, 43, 41]); + * const copy = BinarySearchTree.from(tree); + * ``` + * + * @example Creating a binary search tree from an array like with a custom comparison function + * ```ts + * import { BinarySearchTree, descend } from "@std/data-structures"; + * const tree = BinarySearchTree.from( + * [42, 43, 41], + * { compare: descend } + * ); + * ``` + * + * @typeparam T The type of the values stored in the binary search tree. + * @param collection An array like, an iterable, or existing binary search tree. + * @param options An optional options object to customize the comparison function. + * @returns A new binary search tree created from the passed collection. + */ static from( collection: ArrayLike | Iterable | BinarySearchTree, - options: { + options?: { compare?: (a: T, b: T) => number; }, ): BinarySearchTree; - static from( + /** + * Create a new binary search tree from an array like, an iterable object, or + * an existing binary search tree. + * + * A custom mapping function can be provided to transform the values before + * inserting them into the tree. + * + * A custom comparison function can be provided to sort the values in a + * specific order. A custom mapping function can be provided to transform the + * values before inserting them into the tree. By default, the values are + * sorted in ascending order, unless a {@link BinarySearchTree} is passed, in + * which case the comparison function is copied from the input tree. The + * comparison operator is used to sort the values in the tree after mapping + * the values. + * + * @example Creating a binary search tree from an array like with a custom mapping function + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from( + * [42, 43, 41], + * { map: (value) => value.toString() } + * ); + * ``` + * + * @typeparam T The type of the values in the passed collection. + * @typeparam U The type of the values stored in the binary search tree. + * @typeparam V The type of the `this` value when calling the mapping function. Defaults to `undefined`. + * @param collection An array like, an iterable, or existing binary search tree. + * @param options The options object to customize the mapping and comparison functions. The `thisArg` property can be used to set the `this` value when calling the mapping function. + * @returns A new binary search tree containing the mapped values from the passed collection. + */ + static from( collection: ArrayLike | Iterable | BinarySearchTree, options: { compare?: (a: U, b: U) => number; @@ -169,7 +268,20 @@ export class BinarySearchTree implements Iterable { return result; } - /** The amount of values stored in the binary search tree. */ + /** + * The count of values stored in the binary search tree. + * + * The complexity of this operation is O(1). + * + * @example Getting the size of the tree + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([42, 43, 41]); + * tree.size; // 3 + * ``` + * + * @returns The count of values stored in the binary search tree. + */ get size(): number { return this._size; } @@ -269,16 +381,43 @@ export class BinarySearchTree implements Iterable { } /** - * Adds the value to the binary search tree if it does not already exist in it. - * Returns true if successful. + * Add a value to the binary search tree if it does not already exist in the + * tree. + * + * The complexity of this operation is on average O(log n), where n is the + * number of values in the tree. In the worst case, the complexity is O(n). + * + * @example Inserting values into the tree + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = new BinarySearchTree(); + * tree.insert(42); // true + * tree.insert(42); // false + * ``` + * + * @param value The value to insert into the binary search tree. + * @returns `true` if the value was inserted, `false` if the value already exists in the tree. */ insert(value: T): boolean { return !!this.insertNode(BinarySearchNode, value); } /** - * Removes node value from the binary search tree if found. - * Returns true if found and removed. + * Remove a value from the binary search tree if it exists in the tree. + * + * The complexity of this operation is on average O(log n), where n is the + * number of values in the tree. In the worst case, the complexity is O(n). + * + * @example Removing values from the tree + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([42]); + * tree.remove(42); // true + * tree.remove(42); // false + * ``` + * + * @param value The value to remove from the binary search tree. + * @returns `true` if the value was found and removed, `false` if the value was not found in the tree. */ remove(value: T): boolean { const node: BinarySearchNode | null = this.findNode(value); @@ -286,35 +425,118 @@ export class BinarySearchTree implements Iterable { return node !== null; } - /** Returns node value if found in the binary search tree. */ + /** + * Check if a value exists in the binary search tree. + * + * The complexity of this operation depends on the underlying structure of the + * tree. Refer to the documentation of the structure itself for more details. + * + * @example Finding values in the tree + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([42]); + * tree.find(42); // 42 + * tree.find(43); // null + * ``` + * + * @param value The value to search for in the binary search tree. + * @returns The value if it was found, or null if not found. + */ find(value: T): T | null { return this.findNode(value)?.value ?? null; } - /** Returns the minimum value in the binary search tree or null if empty. */ + /** + * Retrieve the lowest (left most) value in the binary search tree, or null if + * the tree is empty. + * + * The complexity of this operation depends on the underlying structure of the + * tree. Refer to the documentation of the structure itself for more details. + * + * @example Finding the minimum value in the tree + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([42, 43, 41]); + * tree.min(); // 41 + * ``` + * + * @returns The minimum value in the binary search tree, or null if the tree is empty. + */ min(): T | null { return this.root ? this.root.findMinNode().value : null; } - /** Returns the maximum value in the binary search tree or null if empty. */ + /** + * Retrieve the highest (right most) value in the binary search tree, or null + * if the tree is empty. + * + * The complexity of this operation depends on the underlying structure of the + * tree. Refer to the documentation of the structure itself for more details. + * + * @example Finding the maximum value in the tree + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([42, 43, 41]); + * tree.max(); // 43 + * ``` + * + * @returns The maximum value in the binary search tree, or null if the tree is empty. + */ max(): T | null { return this.root ? this.root.findMaxNode().value : null; } - /** Removes all values from the binary search tree. */ + /** + * Remove all values from the binary search tree. + * + * The complexity of this operation is O(1). + * + * @example Clearing the tree + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([42, 43, 41]); + * tree.clear(); + * tree.size; // 0 + * tree.find(42); // null + * ``` + */ clear() { this.root = null; this._size = 0; } - /** Checks if the binary search tree is empty. */ + /** + * Check if the binary search tree is empty. + * + * The complexity of this operation is O(1). + * + * @example Checking if the tree is empty + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = new BinarySearchTree(); + * tree.isEmpty(); // true + * tree.insert(42); + * tree.isEmpty(); // false + * ``` + * + * @returns `true` if the binary search tree is empty, `false` otherwise. + */ isEmpty(): boolean { return this.size === 0; } /** - * Returns an iterator that uses in-order (LNR) tree traversal for - * retrieving values from the binary search tree. + * Create an iterator over this tree that traverses the tree in-order (LNR, + * Left-Node-Right). + * + * @example Using the in-order LNR iterator + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([4, 1, 2, 5, 3]); + * [...tree.lnrValues()] // 1, 2, 3, 4, 5 + * ``` + * + * @returns An iterator that traverses the tree in-order (LNR). */ *lnrValues(): IterableIterator { const nodes: BinarySearchNode[] = []; @@ -332,8 +554,17 @@ export class BinarySearchTree implements Iterable { } /** - * Returns an iterator that uses reverse in-order (RNL) tree traversal for - * retrieving values from the binary search tree. + * Create an iterator over this tree that traverses the tree in reverse + * in-order (RNL, Right-Node-Left). + * + * @example Using the reverse in-order RNL iterator + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([4, 1, 2, 5, 3]); + * [...tree.rnlValues()] // 5, 4, 3, 2, 1 + * ``` + * + * @returns An iterator that traverses the tree in reverse in-order (RNL). */ *rnlValues(): IterableIterator { const nodes: BinarySearchNode[] = []; @@ -351,8 +582,17 @@ export class BinarySearchTree implements Iterable { } /** - * Returns an iterator that uses pre-order (NLR) tree traversal for - * retrieving values from the binary search tree. + * Create an iterator over this tree that traverses the tree in pre-order (NLR, + * Node-Left-Right). + * + * @example Using the pre-order NLR iterator + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([4, 1, 2, 5, 3]); + * [...tree.nlrValues()] // 4, 1, 2, 3, 5 + * ``` + * + * @returns An iterator that traverses the tree in pre-order (NLR). */ *nlrValues(): IterableIterator { const nodes: BinarySearchNode[] = []; @@ -366,8 +606,17 @@ export class BinarySearchTree implements Iterable { } /** - * Returns an iterator that uses post-order (LRN) tree traversal for - * retrieving values from the binary search tree. + * Create an iterator over this tree that traverses the tree in post-order (LRN, + * Left-Right-Node). + * + * @example Using the post-order LRN iterator + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([4, 1, 2, 5, 3]); + * [...tree.lrnValues()] // 3, 2, 1, 5, 4 + * ``` + * + * @returns An iterator that traverses the tree in post-order (LRN). */ *lrnValues(): IterableIterator { const nodes: BinarySearchNode[] = []; @@ -390,8 +639,17 @@ export class BinarySearchTree implements Iterable { } /** - * Returns an iterator that uses level order tree traversal for - * retrieving values from the binary search tree. + * Create an iterator over this tree that traverses the tree in level-order (BFS, + * Breadth-First Search). + * + * @example Using the level-order BFS iterator + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([4, 1, 2, 5, 3]); + * [...tree.lvlValues()] // 4, 1, 5, 2, 3 + * ``` + * + * @returns An iterator that traverses the tree in level-order (BFS). */ *lvlValues(): IterableIterator { const children: BinarySearchNode[] = []; @@ -405,8 +663,19 @@ export class BinarySearchTree implements Iterable { } /** - * Returns an iterator that uses in-order (LNR) tree traversal for - * retrieving values from the binary search tree. + * Create an iterator over this tree that traverses the tree in-order (LNR, + * Left-Node-Right). + * + * @example Using the in-order iterator + * ```ts + * import { BinarySearchTree } from "@std/data-structures"; + * const tree = BinarySearchTree.from([4, 1, 2, 5, 3]); + * [...tree] // 1, 2, 3, 4, 5 + * ``` + * + * See {@link BinarySearchTree#lnrValues}. + * + * @returns An iterator that traverses the tree in-order (LNR). */ *[Symbol.iterator](): IterableIterator { yield* this.lnrValues(); diff --git a/data_structures/comparators.ts b/data_structures/comparators.ts index 18d3953a1764..a9a3adefdb1d 100644 --- a/data_structures/comparators.ts +++ b/data_structures/comparators.ts @@ -2,12 +2,44 @@ // This module is browser compatible. /** This module is browser compatible. */ -/** Compares its two arguments for ascending order using JavaScript's built in comparison operators. */ +/** + * Compare two values in ascending order using JavaScript's built in comparison + * operators. + * + * @example Comparing numbers + * ```ts + * import { ascend } from "@std/data-structures"; + * ascend(1, 2); // -1 + * ascend(2, 1); // 1 + * ascend(1, 1); // 0 + * ``` + * + * @typeparam T The type of the values being compared. + * @param a The left comparison value. + * @param b The right comparison value. + * @returns -1 if `a` is less than `b`, 0 if `a` is equal to `b`, and 1 if `a` is greater than `b`. + */ export function ascend(a: T, b: T): -1 | 0 | 1 { return a < b ? -1 : a > b ? 1 : 0; } -/** Compares its two arguments for descending order using JavaScript's built in comparison operators. */ +/** + * Compare two values in descending order using JavaScript's built in comparison + * operators. + * + * @example Comparing numbers + * ```ts + * import { descend } from "@std/data-structures"; + * descend(1, 2); // 1 + * descend(2, 1); // -1 + * descend(1, 1); // 0 + * ``` + * + * @typeparam T The type of the values being compared. + * @param a The left comparison value. + * @param b The right comparison value. + * @returns -1 if `a` is greater than `b`, 0 if `a` is equal to `b`, and 1 if `a` is less than `b`. + */ export function descend(a: T, b: T): -1 | 0 | 1 { return a < b ? 1 : a > b ? -1 : 0; } diff --git a/data_structures/red_black_tree.ts b/data_structures/red_black_tree.ts index 30d7fa7eed0d..8b72a5ee5faa 100644 --- a/data_structures/red_black_tree.ts +++ b/data_structures/red_black_tree.ts @@ -86,28 +86,120 @@ import { type Direction, RedBlackNode } from "./_red_black_node.ts"; * "helicopter", * ]); * ``` + * + * @typeparam T The type of the values being stored in the tree. */ export class RedBlackTree extends BinarySearchTree { declare protected root: RedBlackNode | null; + /** + * Construct an empty red-black tree. + * + * @example Creating an empty red-black tree + * ```ts + * import { RedBlackTree } from "@std/data-structures"; + * const tree = new RedBlackTree(); + * ``` + * + * @example Creating a red-black tree with a custom comparison function + * ```ts + * import { RedBlackTree, ascend } from "@std/data-structures"; + * const tree = new RedBlackTree<{ price: number, name: string }>( + * (a, b) => ascend(a.price, b.price) || ascend(a.name, b.name) + * ); + * ``` + * + * @param compare A custom comparison function for the values. The default comparison function sorts by ascending order. + */ constructor( compare: (a: T, b: T) => number = ascend, ) { super(compare); } - /** Creates a new red-black tree from an array like or iterable object. */ - static override from( - collection: ArrayLike | Iterable | RedBlackTree, - ): RedBlackTree; + /** + * Create a new red-black tree from an array like, an iterable object, or + * an existing red-black tree. + * + * A custom comparison function can be provided to sort the values in a + * specific order. By default, the values are sorted in ascending order, + * unless a {@link RedBlackTree} is passed, in which case the comparison + * function is copied from the input tree. + * + * @example Creating a red-black tree from an array like + * ```ts + * import { RedBlackTree } from "@std/data-structures"; + * const tree = RedBlackTree.from([3, 10, 13, 4, 6, 7, 1, 14]); + * ``` + * + * @example Creating a red-black tree from an iterable object + * ```ts + * import { RedBlackTree } from "@std/data-structures"; + * const tree = RedBlackTree.from((function*() { + * yield 3; + * yield 10; + * yield 13; + * })()); + * ``` + * + * @example Creating a red-black tree from an existing red-black tree + * ```ts + * import { RedBlackTree } from "@std/data-structures"; + * const tree = RedBlackTree.from([3, 10, 13, 4, 6, 7, 1, 14]); + * const copy = RedBlackTree.from(tree); + * ``` + * + * @example Creating a red-black tree from an array like with a custom comparison function + * ```ts + * import { RedBlackTree, descend } from "@std/data-structures"; + * const tree = RedBlackTree.from([3, 10, 13, 4, 6, 7, 1, 14], { + * compare: descend, + * }); + * ``` + * + * @typeparam T The type of the values being stored in the tree. + * @param collection An array like, an iterable, or existing red-black tree. + * @param options An optional options object to customize the comparison function. + * @returns A new red-black tree with the values from the passed collection. + */ static override from( collection: ArrayLike | Iterable | RedBlackTree, - options: { + options?: { Node?: typeof RedBlackNode; compare?: (a: T, b: T) => number; }, ): RedBlackTree; - static override from( + /** + * Create a new red-black tree from an array like, an iterable object, or + * an existing red-black tree. + * + * A custom mapping function can be provided to transform the values before + * inserting them into the tree. + * + * A custom comparison function can be provided to sort the values in a + * specific order. A custom mapping function can be provided to transform the + * values before inserting them into the tree. By default, the values are + * sorted in ascending order, unless a {@link RedBlackTree} is passed, in + * which case the comparison function is copied from the input tree. The + * comparison operator is used to sort the values in the tree after mapping + * the values. + * + * @example Creating a red-black tree from an array like with a custom mapping function + * ```ts + * import { RedBlackTree } from "@std/data-structures"; + * const tree = RedBlackTree.from([3, 10, 13, 4, 6, 7, 1, 14], { + * map: (value) => value.toString(), + * }); + * ``` + + * @typeparam T The type of the values in the passed collection. + * @typeparam U The type of the values being stored in the red-black tree. + * @typeparam V The type of the `this` context in the mapping function. Defaults to `undefined`. + * @param collection An array like, an iterable, or existing red-black tree. + * @param options The options object to customize the mapping and comparison functions. The `thisArg` property can be used to set the `this` value when calling the mapping function. + * @returns A new red-black tree with the mapped values from the passed collection. + */ + static override from( collection: ArrayLike | Iterable | RedBlackTree, options: { compare?: (a: U, b: U) => number; @@ -213,8 +305,21 @@ export class RedBlackTree extends BinarySearchTree { } /** - * Adds the value to the binary search tree if it does not already exist in it. - * Returns true if successful. + * Add a value to the red-black tree if it does not already exist in the tree. + * + * The complexity of this operation is on average and at worst O(log n), where + * n is the number of values in the tree. + * + * @example Inserting a value into the tree + * ```ts + * import { RedBlackTree } from "@std/data-structures"; + * const tree = new RedBlackTree(); + * tree.insert(42); // true + * tree.insert(42); // false + * ``` + * + * @param value The value to insert into the tree. + * @returns `true` if the value was inserted, `false` if the value already exists in the tree. */ override insert(value: T): boolean { let node = this.insertNode(RedBlackNode, value) as (RedBlackNode | null); @@ -250,8 +355,21 @@ export class RedBlackTree extends BinarySearchTree { } /** - * Removes node value from the binary search tree if found. - * Returns true if found and removed. + * Remove a value from the red-black tree if it exists in the tree. + * + * The complexity of this operation is on average and at worst O(log n), where + * n is the number of values in the tree. + * + * @example Removing values from the tree + * ```ts + * import { RedBlackTree } from "@std/data-structures"; + * const tree = RedBlackTree.from([42]); + * tree.remove(42); // true + * tree.remove(42); // false + * ``` + * + * @param value The value to remove from the tree. + * @returns `true` if the value was found and removed, `false` if the value was not found in the tree. */ override remove(value: T): boolean { const node = this.findNode(value) as (RedBlackNode | null);