diff --git a/collections/README.md b/collections/README.md index 29d1e59fb970..818d6888afd4 100644 --- a/collections/README.md +++ b/collections/README.md @@ -943,15 +943,15 @@ assertEquals([...words], ["truck", "tank", "car"]); assertEquals([...words], []); ``` -### BSTree +### BinarySearchTree An unbalanced binary search tree. The values are in ascending order by default, using JavaScript's built in comparison operators to sort the values. 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 RBTree for an example of how BSTree can be extended to -create a self balancing binary search tree. +balancing tree. See RedBlackTree for an example of how BinarySearchTree can be +extended to create a self balancing binary search tree. | Method | Average Case | Worst Case | | ------------- | ------------ | ---------- | @@ -964,13 +964,13 @@ create a self balancing binary search tree. ```ts import { ascend, - BSTree, + BinarySearchTree, descend, -} from "https://deno.land/std@$STD_VERSION/collections/bs_tree.ts"; +} from "https://deno.land/std@$STD_VERSION/collections/binary_search_tree.ts"; import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts"; const values = [3, 10, 13, 4, 6, 7, 1, 14]; -const tree = new BSTree(); +const tree = new BinarySearchTree(); values.forEach((value) => tree.insert(value)); assertEquals([...tree], [1, 3, 4, 6, 7, 10, 13, 14]); assertEquals(tree.min(), 1); @@ -981,7 +981,7 @@ assertEquals(tree.remove(42), false); assertEquals(tree.remove(7), true); assertEquals([...tree], [1, 3, 4, 6, 10, 13, 14]); -const invertedTree = new BSTree(descend); +const invertedTree = new BinarySearchTree(descend); values.forEach((value) => invertedTree.insert(value)); assertEquals([...invertedTree], [14, 13, 10, 7, 6, 4, 3, 1]); assertEquals(invertedTree.min(), 14); @@ -992,7 +992,7 @@ assertEquals(invertedTree.remove(42), false); assertEquals(invertedTree.remove(7), true); assertEquals([...invertedTree], [14, 13, 10, 6, 4, 3, 1]); -const words = new BSTree((a, b) => +const words = new BinarySearchTree((a, b) => ascend(a.length, b.length) || ascend(a, b) ); ["truck", "car", "helicopter", "tank", "train", "suv", "semi", "van"] @@ -1024,7 +1024,7 @@ assertEquals([...words], [ ]); ``` -### RBTree +### RedBlackTree A red-black tree. This is a kind of self-balancing binary search tree. The values are in ascending order by default, using JavaScript's built in comparison @@ -1047,12 +1047,12 @@ Trees, so they can provide faster lookups. import { ascend, descend, - RBTree, -} from "https://deno.land/std@$STD_VERSION/collections/rb_tree.ts"; + RedBlackTree, +} from "https://deno.land/std@$STD_VERSION/collections/red_black_tree.ts"; import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts"; const values = [3, 10, 13, 4, 6, 7, 1, 14]; -const tree = new RBTree(); +const tree = new RedBlackTree(); values.forEach((value) => tree.insert(value)); assertEquals([...tree], [1, 3, 4, 6, 7, 10, 13, 14]); assertEquals(tree.min(), 1); @@ -1063,7 +1063,7 @@ assertEquals(tree.remove(42), false); assertEquals(tree.remove(7), true); assertEquals([...tree], [1, 3, 4, 6, 10, 13, 14]); -const invertedTree = new RBTree(descend); +const invertedTree = new RedBlackTree(descend); values.forEach((value) => invertedTree.insert(value)); assertEquals([...invertedTree], [14, 13, 10, 7, 6, 4, 3, 1]); assertEquals(invertedTree.min(), 14); @@ -1074,7 +1074,7 @@ assertEquals(invertedTree.remove(42), false); assertEquals(invertedTree.remove(7), true); assertEquals([...invertedTree], [14, 13, 10, 6, 4, 3, 1]); -const words = new RBTree((a, b) => +const words = new RedBlackTree((a, b) => ascend(a.length, b.length) || ascend(a, b) ); ["truck", "car", "helicopter", "tank", "train", "suv", "semi", "van"] diff --git a/collections/binary_search_node.ts b/collections/binary_search_node.ts new file mode 100644 index 000000000000..2af9c28290af --- /dev/null +++ b/collections/binary_search_node.ts @@ -0,0 +1,56 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +/** This module is browser compatible. */ + +export type Direction = "left" | "right"; + +export class BinarySearchNode { + left: BinarySearchNode | null; + right: BinarySearchNode | null; + constructor(public parent: BinarySearchNode | null, public value: T) { + this.left = null; + this.right = null; + } + + static from(node: BinarySearchNode): BinarySearchNode { + const copy: BinarySearchNode = new BinarySearchNode( + node.parent, + node.value, + ); + copy.left = node.left; + copy.right = node.right; + return copy; + } + + directionFromParent(): Direction | null { + return this.parent === null + ? null + : this === this.parent.left + ? "left" + : this === this.parent.right + ? "right" + : null; + } + + findMinNode(): BinarySearchNode { + let minNode: BinarySearchNode | null = this.left; + while (minNode?.left) minNode = minNode.left; + return minNode ?? this; + } + + findMaxNode(): BinarySearchNode { + let maxNode: BinarySearchNode | null = this.right; + while (maxNode?.right) maxNode = maxNode.right; + return maxNode ?? this; + } + + findSuccessorNode(): BinarySearchNode | null { + if (this.right !== null) return this.right.findMinNode(); + let parent: BinarySearchNode | null = this.parent; + let direction: Direction | null = this.directionFromParent(); + while (parent && direction === "right") { + direction = parent.directionFromParent(); + parent = parent.parent; + } + return parent; + } +} diff --git a/collections/bs_node_test.ts b/collections/binary_search_node_test.ts similarity index 64% rename from collections/bs_node_test.ts rename to collections/binary_search_node_test.ts index b010780b3c46..a8aa8e738585 100644 --- a/collections/bs_node_test.ts +++ b/collections/binary_search_node_test.ts @@ -1,16 +1,16 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. import { assertEquals, assertStrictEquals } from "../testing/asserts.ts"; -import { BSNode } from "./bs_node.ts"; +import { BinarySearchNode } from "./binary_search_node.ts"; -let parent: BSNode; -let child: BSNode; +let parent: BinarySearchNode; +let child: BinarySearchNode; function beforeEach() { - parent = new BSNode(null, 5); - child = new BSNode(parent, 7); + parent = new BinarySearchNode(null, 5); + child = new BinarySearchNode(parent, 7); parent.right = child; } -Deno.test("[collections/BSNode] constructor", () => { +Deno.test("[collections/BinarySearchNode] constructor", () => { beforeEach(); assertStrictEquals(parent.parent, null); assertStrictEquals(parent.left, null); @@ -23,10 +23,10 @@ Deno.test("[collections/BSNode] constructor", () => { assertStrictEquals(child.value, 7); }); -Deno.test("[collections/BSNode] from", () => { +Deno.test("[collections/BinarySearchNode] from", () => { beforeEach(); - const parentClone: BSNode = BSNode.from(parent); - const childClone: BSNode = BSNode.from(child); + const parentClone: BinarySearchNode = BinarySearchNode.from(parent); + const childClone: BinarySearchNode = BinarySearchNode.from(child); assertStrictEquals(parentClone.parent, null); assertStrictEquals(parentClone.left, null); @@ -39,9 +39,9 @@ Deno.test("[collections/BSNode] from", () => { assertStrictEquals(childClone.value, 7); }); -Deno.test("[collections/BSNode] directionFromParent", () => { +Deno.test("[collections/BinarySearchNode] directionFromParent", () => { beforeEach(); - const child2 = new BSNode(parent, 3); + const child2 = new BinarySearchNode(parent, 3); assertEquals(child2.directionFromParent(), null); parent.left = child2; assertEquals(child2.directionFromParent(), "left"); @@ -49,41 +49,41 @@ Deno.test("[collections/BSNode] directionFromParent", () => { assertEquals(child.directionFromParent(), "right"); }); -Deno.test("[collections/BSNode] findMinNode", () => { +Deno.test("[collections/BinarySearchNode] findMinNode", () => { beforeEach(); assertStrictEquals(parent.findMinNode(), parent); - const child2 = new BSNode(parent, 3); + const child2 = new BinarySearchNode(parent, 3); parent.left = child2; assertStrictEquals(parent.findMinNode(), child2); - const child3 = new BSNode(child2, 4); + const child3 = new BinarySearchNode(child2, 4); child2.right = child3; assertStrictEquals(parent.findMinNode(), child2); - const child4 = new BSNode(child2, 2); + const child4 = new BinarySearchNode(child2, 2); child2.left = child4; assertStrictEquals(parent.findMinNode(), child4); }); -Deno.test("[collections/BSNode] findMaxNode", () => { +Deno.test("[collections/BinarySearchNode] findMaxNode", () => { beforeEach(); assertStrictEquals(parent.findMaxNode(), child); - const child2 = new BSNode(child, 6); + const child2 = new BinarySearchNode(child, 6); child.left = child2; assertStrictEquals(parent.findMaxNode(), child); - const child3 = new BSNode(child2, 6.5); + const child3 = new BinarySearchNode(child2, 6.5); child2.right = child3; assertStrictEquals(parent.findMaxNode(), child); - const child4 = new BSNode(child2, 8); + const child4 = new BinarySearchNode(child2, 8); child.right = child4; assertStrictEquals(parent.findMaxNode(), child4); parent.right = null; assertStrictEquals(parent.findMaxNode(), parent); }); -Deno.test("[collections/BSNode] findSuccessorNode", () => { +Deno.test("[collections/BinarySearchNode] findSuccessorNode", () => { beforeEach(); assertStrictEquals(parent.findSuccessorNode(), child); assertStrictEquals(child.findSuccessorNode(), null); - const child2 = new BSNode(child, 6); + const child2 = new BinarySearchNode(child, 6); child.left = child2; assertStrictEquals(parent.findSuccessorNode(), child2); assertStrictEquals(child.findSuccessorNode(), null); diff --git a/collections/binary_search_tree.ts b/collections/binary_search_tree.ts new file mode 100644 index 000000000000..114e31f4d45f --- /dev/null +++ b/collections/binary_search_tree.ts @@ -0,0 +1,331 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +/** This module is browser compatible. */ + +import { ascend } from "./_comparators.ts"; +import { BinarySearchNode, Direction } from "./binary_search_node.ts"; +export * from "./_comparators.ts"; + +/** + * An unbalanced binary search tree. The values are in ascending order by default, + * using JavaScript's built in comparison operators to sort the values. + */ +export class BinarySearchTree implements Iterable { + protected root: BinarySearchNode | null = null; + protected _size = 0; + 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; + static from( + collection: ArrayLike | Iterable | BinarySearchTree, + options: { + compare?: (a: T, b: T) => number; + }, + ): BinarySearchTree; + static from( + collection: ArrayLike | Iterable | BinarySearchTree, + options: { + compare?: (a: U, b: U) => number; + map: (value: T, index: number) => U; + thisArg?: V; + }, + ): BinarySearchTree; + static from( + collection: ArrayLike | Iterable | BinarySearchTree, + options?: { + compare?: (a: U, b: U) => number; + map?: (value: T, index: number) => U; + thisArg?: V; + }, + ): BinarySearchTree { + let result: BinarySearchTree; + let unmappedValues: ArrayLike | Iterable = []; + if (collection instanceof BinarySearchTree) { + result = new BinarySearchTree( + options?.compare ?? + (collection as unknown as BinarySearchTree).compare, + ); + if (options?.compare || options?.map) { + unmappedValues = collection; + } else { + const nodes: BinarySearchNode[] = []; + if (collection.root) { + result.root = BinarySearchNode.from( + collection.root as unknown as BinarySearchNode, + ); + nodes.push(result.root); + } + while (nodes.length) { + const node: BinarySearchNode = nodes.pop()!; + const left: BinarySearchNode | null = node.left + ? BinarySearchNode.from(node.left) + : null; + const right: BinarySearchNode | null = node.right + ? BinarySearchNode.from(node.right) + : null; + + if (left) { + left.parent = node; + nodes.push(left); + } + if (right) { + right.parent = node; + nodes.push(right); + } + } + } + } else { + result = (options?.compare + ? new BinarySearchTree(options.compare) + : new BinarySearchTree()) as BinarySearchTree; + unmappedValues = collection; + } + const values: Iterable = options?.map + ? Array.from(unmappedValues, options.map, options.thisArg) + : unmappedValues as U[]; + for (const value of values) result.insert(value); + return result; + } + + /** The amount of values stored in the binary search tree. */ + get size(): number { + return this._size; + } + + protected findNode(value: T): BinarySearchNode | null { + let node: BinarySearchNode | null = this.root; + while (node) { + const order: number = this.compare(value as T, node.value); + if (order === 0) break; + const direction: "left" | "right" = order < 0 ? "left" : "right"; + node = node[direction]; + } + return node; + } + + protected rotateNode(node: BinarySearchNode, direction: Direction) { + const replacementDirection: Direction = direction === "left" + ? "right" + : "left"; + if (!node[replacementDirection]) { + throw new TypeError( + `cannot rotate ${direction} without ${replacementDirection} child`, + ); + } + const replacement: BinarySearchNode = node[replacementDirection]!; + node[replacementDirection] = replacement[direction] ?? null; + if (replacement[direction]) replacement[direction]!.parent = node; + replacement.parent = node.parent; + if (node.parent) { + const parentDirection: Direction = node === node.parent[direction] + ? direction + : replacementDirection; + node.parent[parentDirection] = replacement; + } else { + this.root = replacement; + } + replacement[direction] = node; + node.parent = replacement; + } + + protected insertNode( + Node: typeof BinarySearchNode, + value: T, + ): BinarySearchNode | null { + if (!this.root) { + this.root = new Node(null, value); + this._size++; + return this.root; + } else { + let node: BinarySearchNode = this.root; + while (true) { + const order: number = this.compare(value, node.value); + if (order === 0) break; + const direction: Direction = order < 0 ? "left" : "right"; + if (node[direction]) { + node = node[direction]!; + } else { + node[direction] = new Node(node, value); + this._size++; + return node[direction]; + } + } + } + return null; + } + + protected removeNode( + value: T, + ): BinarySearchNode | null { + let removeNode: BinarySearchNode | null = this.findNode(value); + if (removeNode) { + const successorNode: BinarySearchNode | null = + !removeNode.left || !removeNode.right + ? removeNode + : removeNode.findSuccessorNode()!; + const replacementNode: BinarySearchNode | null = successorNode.left ?? + successorNode.right; + if (replacementNode) replacementNode.parent = successorNode.parent; + + if (!successorNode.parent) { + this.root = replacementNode; + } else { + successorNode.parent[successorNode.directionFromParent()!] = + replacementNode; + } + + if (successorNode !== removeNode) { + removeNode.value = successorNode.value; + removeNode = successorNode; + } + this._size--; + } + return removeNode; + } + + /** + * Adds the value to the binary search tree if it does not already exist in it. + * Returns true if successful. + */ + 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(value: T): boolean { + return !!this.removeNode(value); + } + + /** Returns node value if found in the binary search tree. */ + find(value: T): T | null { + return this.findNode(value)?.value ?? null; + } + + /** Returns the minimum value in the binary search tree or null if 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. */ + max(): T | null { + return this.root ? this.root.findMaxNode().value : null; + } + + /** Removes all values from the binary search tree. */ + clear(): void { + this.root = null; + this._size = 0; + } + + /** Checks if the binary search tree is empty. */ + isEmpty(): boolean { + return this.size === 0; + } + + /** + * Returns an iterator that uses in-order (LNR) tree traversal for + * retrieving values from the binary search tree. + */ + *lnrValues(): IterableIterator { + const nodes: BinarySearchNode[] = []; + let node: BinarySearchNode | null = this.root; + while (nodes.length || node) { + if (node) { + nodes.push(node); + node = node.left; + } else { + node = nodes.pop()!; + yield node.value; + node = node.right; + } + } + } + + /** + * Returns an iterator that uses reverse in-order (RNL) tree traversal for + * retrieving values from the binary search tree. + */ + *rnlValues(): IterableIterator { + const nodes: BinarySearchNode[] = []; + let node: BinarySearchNode | null = this.root; + while (nodes.length || node) { + if (node) { + nodes.push(node); + node = node.right; + } else { + node = nodes.pop()!; + yield node.value; + node = node.left; + } + } + } + + /** + * Returns an iterator that uses pre-order (NLR) tree traversal for + * retrieving values from the binary search tree. + */ + *nlrValues(): IterableIterator { + const nodes: BinarySearchNode[] = []; + if (this.root) nodes.push(this.root); + while (nodes.length) { + const node: BinarySearchNode = nodes.pop()!; + yield node.value; + if (node.right) nodes.push(node.right); + if (node.left) nodes.push(node.left); + } + } + + /** + * Returns an iterator that uses post-order (LRN) tree traversal for + * retrieving values from the binary search tree. + */ + *lrnValues(): IterableIterator { + const nodes: BinarySearchNode[] = []; + let node: BinarySearchNode | null = this.root; + let lastNodeVisited: BinarySearchNode | null = null; + while (nodes.length || node) { + if (node) { + nodes.push(node); + node = node.left; + } else { + const lastNode: BinarySearchNode = nodes[nodes.length - 1]; + if (lastNode.right && lastNode.right !== lastNodeVisited) { + node = lastNode.right; + } else { + yield lastNode.value; + lastNodeVisited = nodes.pop()!; + } + } + } + } + + /** + * Returns an iterator that uses level order tree traversal for + * retrieving values from the binary search tree. + */ + *lvlValues(): IterableIterator { + const children: BinarySearchNode[] = []; + let cursor: BinarySearchNode | null = this.root; + while (cursor) { + yield cursor.value; + if (cursor.left) children.push(cursor.left); + if (cursor.right) children.push(cursor.right); + cursor = children.shift() ?? null; + } + } + + /** + * Returns an iterator that uses in-order (LNR) tree traversal for + * retrieving values from the binary search tree. + */ + *[Symbol.iterator](): IterableIterator { + yield* this.lnrValues(); + } +} diff --git a/collections/bs_tree_test.ts b/collections/binary_search_tree_test.ts similarity index 87% rename from collections/bs_tree_test.ts rename to collections/binary_search_tree_test.ts index 411efc6c3187..4ed5bd2f58b8 100644 --- a/collections/bs_tree_test.ts +++ b/collections/binary_search_tree_test.ts @@ -1,6 +1,6 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. import { assertEquals, assertStrictEquals } from "../testing/asserts.ts"; -import { ascend, BSTree, descend } from "./bs_tree.ts"; +import { ascend, BinarySearchTree, descend } from "./binary_search_tree.ts"; class MyMath { multiply(a: number, b: number): number { @@ -13,8 +13,11 @@ interface Container { values: number[]; } -Deno.test("[collections/BSTree] with default ascend comparator", () => { - const trees: BSTree[] = [new BSTree(), new BSTree()]; +Deno.test("[collections/BinarySearchTree] with default ascend comparator", () => { + const trees: BinarySearchTree[] = [ + new BinarySearchTree(), + new BinarySearchTree(), + ]; const values: number[] = [-10, 9, -1, 100, 1, 0, -100, 10, -9]; const expectedMin: number[][] = [ @@ -130,8 +133,11 @@ Deno.test("[collections/BSTree] with default ascend comparator", () => { } }); -Deno.test("[collections/BSTree] with descend comparator", () => { - const trees: BSTree[] = [new BSTree(descend), new BSTree(descend)]; +Deno.test("[collections/BinarySearchTree] with descend comparator", () => { + const trees: BinarySearchTree[] = [ + new BinarySearchTree(descend), + new BinarySearchTree(descend), + ]; const values: number[] = [-10, 9, -1, 100, 1, 0, -100, 10, -9]; const expectedMin: number[][] = [ @@ -247,8 +253,8 @@ Deno.test("[collections/BSTree] with descend comparator", () => { } }); -Deno.test("[collections/BSTree] containing objects", () => { - const tree: BSTree = new BSTree(( +Deno.test("[collections/BinarySearchTree] containing objects", () => { + const tree: BinarySearchTree = new BinarySearchTree(( a: Container, b: Container, ) => ascend(a.id, b.id)); @@ -307,23 +313,23 @@ Deno.test("[collections/BSTree] containing objects", () => { assertEquals(tree.isEmpty(), true); }); -Deno.test("[collections/BSTree] from Iterable", () => { +Deno.test("[collections/BinarySearchTree] from Iterable", () => { const values: number[] = [-10, 9, -1, 100, 9, 1, 0, 9, -100, 10, -9]; const originalValues: number[] = Array.from(values); const expected: number[] = [-100, -10, -9, -1, 0, 1, 9, 10, 100]; - let tree: BSTree = BSTree.from(values); + let tree: BinarySearchTree = BinarySearchTree.from(values); assertEquals(values, originalValues); assertEquals([...tree], expected); assertEquals([...tree.nlrValues()], [-10, -100, 9, -1, -9, 1, 0, 100, 10]); assertEquals([...tree.lvlValues()], [-10, -100, 9, -1, 100, -9, 1, 10, 0]); - tree = BSTree.from(values, { compare: descend }); + tree = BinarySearchTree.from(values, { compare: descend }); assertEquals(values, originalValues); assertEquals([...tree].reverse(), expected); assertEquals([...tree.nlrValues()], [-10, 9, 100, 10, -1, 1, 0, -9, -100]); assertEquals([...tree.lvlValues()], [-10, 9, -100, 100, -1, 10, 1, -9, 0]); - tree = BSTree.from(values, { + tree = BinarySearchTree.from(values, { map: (v: number) => 2 * v, }); assertEquals([...tree], expected.map((v: number) => 2 * v)); @@ -331,7 +337,7 @@ Deno.test("[collections/BSTree] from Iterable", () => { assertEquals([...tree.lvlValues()], [-20, -200, 18, -2, 200, -18, 2, 20, 0]); const math = new MyMath(); - tree = BSTree.from(values, { + tree = BinarySearchTree.from(values, { map: function (this: MyMath, v: number) { return this.multiply(3, v); }, @@ -342,7 +348,7 @@ Deno.test("[collections/BSTree] from Iterable", () => { assertEquals([...tree.nlrValues()], [-30, -300, 27, -3, -27, 3, 0, 300, 30]); assertEquals([...tree.lvlValues()], [-30, -300, 27, -3, 300, -27, 3, 30, 0]); - tree = BSTree.from(values, { + tree = BinarySearchTree.from(values, { compare: descend, map: (v: number) => 2 * v, }); @@ -351,7 +357,7 @@ Deno.test("[collections/BSTree] from Iterable", () => { assertEquals([...tree.nlrValues()], [-20, 18, 200, 20, -2, 2, 0, -18, -200]); assertEquals([...tree.lvlValues()], [-20, 18, -200, 200, -2, 20, 2, -18, 0]); - tree = BSTree.from(values, { + tree = BinarySearchTree.from(values, { compare: descend, map: function (this: MyMath, v: number) { return this.multiply(3, v); @@ -364,31 +370,31 @@ Deno.test("[collections/BSTree] from Iterable", () => { assertEquals([...tree.lvlValues()], [-30, 27, -300, 300, -3, 30, 3, -27, 0]); }); -Deno.test("[collections/BSTree] from BSTree with default ascend comparator", () => { +Deno.test("[collections/BinarySearchTree] from BinarySearchTree with default ascend comparator", () => { const values: number[] = [-10, 9, -1, 100, 9, 1, 0, 9, -100, 10, -9]; const expected: number[] = [-100, -10, -9, -1, 0, 1, 9, 10, 100]; - const originalTree: BSTree = new BSTree(); + const originalTree: BinarySearchTree = new BinarySearchTree(); for (const value of values) originalTree.insert(value); - let tree: BSTree = BSTree.from(originalTree); + let tree: BinarySearchTree = BinarySearchTree.from(originalTree); assertEquals([...originalTree], expected); assertEquals([...tree], expected); assertEquals([...tree.nlrValues()], [...originalTree.nlrValues()]); assertEquals([...tree.lvlValues()], [...originalTree.lvlValues()]); - tree = BSTree.from(originalTree, { compare: descend }); + tree = BinarySearchTree.from(originalTree, { compare: descend }); assertEquals([...originalTree], expected); assertEquals([...tree].reverse(), expected); assertEquals([...tree.nlrValues()], expected); assertEquals([...tree.lvlValues()], expected); - tree = BSTree.from(originalTree, { + tree = BinarySearchTree.from(originalTree, { map: (v: number) => 2 * v, }); assertEquals([...originalTree], expected); assertEquals([...tree], expected.map((v: number) => 2 * v)); const math = new MyMath(); - tree = BSTree.from(originalTree, { + tree = BinarySearchTree.from(originalTree, { map: function (this: MyMath, v: number) { return this.multiply(3, v); }, @@ -397,14 +403,14 @@ Deno.test("[collections/BSTree] from BSTree with default ascend comparator", () assertEquals([...originalTree], expected); assertEquals([...tree], expected.map((v: number) => 3 * v)); - tree = BSTree.from(originalTree, { + tree = BinarySearchTree.from(originalTree, { compare: descend, map: (v: number) => 2 * v, }); assertEquals([...originalTree], expected); assertEquals([...tree].reverse(), expected.map((v: number) => 2 * v)); - tree = BSTree.from(originalTree, { + tree = BinarySearchTree.from(originalTree, { compare: descend, map: function (this: MyMath, v: number) { return this.multiply(3, v); @@ -415,31 +421,31 @@ Deno.test("[collections/BSTree] from BSTree with default ascend comparator", () assertEquals([...tree].reverse(), expected.map((v: number) => 3 * v)); }); -Deno.test("[collections/BSTree] from BSTree with descend comparator", () => { +Deno.test("[collections/BinarySearchTree] from BinarySearchTree with descend comparator", () => { const values: number[] = [-10, 9, -1, 100, 9, 1, 0, 9, -100, 10, -9]; const expected: number[] = [100, 10, 9, 1, 0, -1, -9, -10, -100]; - const originalTree = new BSTree(descend); + const originalTree = new BinarySearchTree(descend); for (const value of values) originalTree.insert(value); - let tree: BSTree = BSTree.from(originalTree); + let tree: BinarySearchTree = BinarySearchTree.from(originalTree); assertEquals([...originalTree], expected); assertEquals([...tree], expected); assertEquals([...tree.nlrValues()], [...originalTree.nlrValues()]); assertEquals([...tree.lvlValues()], [...originalTree.lvlValues()]); - tree = BSTree.from(originalTree, { compare: ascend }); + tree = BinarySearchTree.from(originalTree, { compare: ascend }); assertEquals([...originalTree], expected); assertEquals([...tree].reverse(), expected); assertEquals([...tree.nlrValues()], expected); assertEquals([...tree.lvlValues()], expected); - tree = BSTree.from(originalTree, { + tree = BinarySearchTree.from(originalTree, { map: (v: number) => 2 * v, }); assertEquals([...originalTree], expected); assertEquals([...tree], expected.map((v: number) => 2 * v)); const math = new MyMath(); - tree = BSTree.from(originalTree, { + tree = BinarySearchTree.from(originalTree, { map: function (this: MyMath, v: number) { return this.multiply(3, v); }, @@ -448,14 +454,14 @@ Deno.test("[collections/BSTree] from BSTree with descend comparator", () => { assertEquals([...originalTree], expected); assertEquals([...tree], expected.map((v: number) => 3 * v)); - tree = BSTree.from(originalTree, { + tree = BinarySearchTree.from(originalTree, { compare: ascend, map: (v: number) => 2 * v, }); assertEquals([...originalTree], expected); assertEquals([...tree].reverse(), expected.map((v: number) => 2 * v)); - tree = BSTree.from(originalTree, { + tree = BinarySearchTree.from(originalTree, { compare: ascend, map: function (this: MyMath, v: number) { return this.multiply(3, v); @@ -466,9 +472,9 @@ Deno.test("[collections/BSTree] from BSTree with descend comparator", () => { assertEquals([...tree].reverse(), expected.map((v: number) => 3 * v)); }); -Deno.test("[collections/BSTree] README example", () => { +Deno.test("[collections/BinarySearchTree] README example", () => { const values = [3, 10, 13, 4, 6, 7, 1, 14]; - const tree = new BSTree(); + const tree = new BinarySearchTree(); values.forEach((value) => tree.insert(value)); assertEquals([...tree], [1, 3, 4, 6, 7, 10, 13, 14]); assertEquals(tree.min(), 1); @@ -479,7 +485,7 @@ Deno.test("[collections/BSTree] README example", () => { assertEquals(tree.remove(7), true); assertEquals([...tree], [1, 3, 4, 6, 10, 13, 14]); - const invertedTree = new BSTree(descend); + const invertedTree = new BinarySearchTree(descend); values.forEach((value) => invertedTree.insert(value)); assertEquals([...invertedTree], [14, 13, 10, 7, 6, 4, 3, 1]); assertEquals(invertedTree.min(), 14); @@ -490,7 +496,7 @@ Deno.test("[collections/BSTree] README example", () => { assertEquals(invertedTree.remove(7), true); assertEquals([...invertedTree], [14, 13, 10, 6, 4, 3, 1]); - const words = new BSTree((a, b) => + const words = new BinarySearchTree((a, b) => ascend(a.length, b.length) || ascend(a, b) ); ["truck", "car", "helicopter", "tank", "train", "suv", "semi", "van"] diff --git a/collections/bs_node.ts b/collections/bs_node.ts index 9526da67a165..7cd2ce7ba01e 100644 --- a/collections/bs_node.ts +++ b/collections/bs_node.ts @@ -1,53 +1,12 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. /** This module is browser compatible. */ -export type direction = "left" | "right"; +import { BinarySearchNode, Direction } from "./binary_search_node.ts"; -export class BSNode { - left: BSNode | null; - right: BSNode | null; - constructor(public parent: BSNode | null, public value: T) { - this.left = null; - this.right = null; - } +/** @deprecated use Direction instead */ +export type { Direction as direction }; - static from(node: BSNode): BSNode { - const copy: BSNode = new BSNode(node.parent, node.value); - copy.left = node.left; - copy.right = node.right; - return copy; - } +export type { Direction }; - directionFromParent(): direction | null { - return this.parent === null - ? null - : this === this.parent.left - ? "left" - : this === this.parent.right - ? "right" - : null; - } - - findMinNode(): BSNode { - let minNode: BSNode | null = this.left; - while (minNode?.left) minNode = minNode.left; - return minNode ?? this; - } - - findMaxNode(): BSNode { - let maxNode: BSNode | null = this.right; - while (maxNode?.right) maxNode = maxNode.right; - return maxNode ?? this; - } - - findSuccessorNode(): BSNode | null { - if (this.right !== null) return this.right.findMinNode(); - let parent: BSNode | null = this.parent; - let direction: direction | null = this.directionFromParent(); - while (parent && direction === "right") { - direction = parent.directionFromParent(); - parent = parent.parent; - } - return parent; - } -} +/** @deprecated use BinarySearchNode instead */ +export { BinarySearchNode as BSNode }; diff --git a/collections/bs_tree.ts b/collections/bs_tree.ts index 12b10323ead8..ca4953a8ffaa 100644 --- a/collections/bs_tree.ts +++ b/collections/bs_tree.ts @@ -1,325 +1,8 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. /** This module is browser compatible. */ -import { ascend } from "./_comparators.ts"; -import { BSNode, direction } from "./bs_node.ts"; +import { BinarySearchTree } from "./binary_search_tree.ts"; export * from "./_comparators.ts"; -/** - * An unbalanced binary search tree. The values are in ascending order by default, - * using JavaScript's built in comparison operators to sort the values. - */ -export class BSTree implements Iterable { - protected root: BSNode | null = null; - protected _size = 0; - 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 | BSTree, - ): BSTree; - static from( - collection: ArrayLike | Iterable | BSTree, - options: { - compare?: (a: T, b: T) => number; - }, - ): BSTree; - static from( - collection: ArrayLike | Iterable | BSTree, - options: { - compare?: (a: U, b: U) => number; - map: (value: T, index: number) => U; - thisArg?: V; - }, - ): BSTree; - static from( - collection: ArrayLike | Iterable | BSTree, - options?: { - compare?: (a: U, b: U) => number; - map?: (value: T, index: number) => U; - thisArg?: V; - }, - ): BSTree { - let result: BSTree; - let unmappedValues: ArrayLike | Iterable = []; - if (collection instanceof BSTree) { - result = new BSTree( - options?.compare ?? (collection as unknown as BSTree).compare, - ); - if (options?.compare || options?.map) { - unmappedValues = collection; - } else { - const nodes: BSNode[] = []; - if (collection.root) { - result.root = BSNode.from(collection.root as unknown as BSNode); - nodes.push(result.root); - } - while (nodes.length) { - const node: BSNode = nodes.pop()!; - const left: BSNode | null = node.left - ? BSNode.from(node.left) - : null; - const right: BSNode | null = node.right - ? BSNode.from(node.right) - : null; - - if (left) { - left.parent = node; - nodes.push(left); - } - if (right) { - right.parent = node; - nodes.push(right); - } - } - } - } else { - result = (options?.compare - ? new BSTree(options.compare) - : new BSTree()) as BSTree; - unmappedValues = collection; - } - const values: Iterable = options?.map - ? Array.from(unmappedValues, options.map, options.thisArg) - : unmappedValues as U[]; - for (const value of values) result.insert(value); - return result; - } - - /** The amount of values stored in the binary search tree. */ - get size(): number { - return this._size; - } - - protected findNode(value: T): BSNode | null { - let node: BSNode | null = this.root; - while (node) { - const order: number = this.compare(value as T, node.value); - if (order === 0) break; - const direction: "left" | "right" = order < 0 ? "left" : "right"; - node = node[direction]; - } - return node; - } - - protected rotateNode(node: BSNode, direction: direction) { - const replacementDirection: direction = direction === "left" - ? "right" - : "left"; - if (!node[replacementDirection]) { - throw new TypeError( - `cannot rotate ${direction} without ${replacementDirection} child`, - ); - } - const replacement: BSNode = node[replacementDirection]!; - node[replacementDirection] = replacement[direction] ?? null; - if (replacement[direction]) replacement[direction]!.parent = node; - replacement.parent = node.parent; - if (node.parent) { - const parentDirection: direction = node === node.parent[direction] - ? direction - : replacementDirection; - node.parent[parentDirection] = replacement; - } else { - this.root = replacement; - } - replacement[direction] = node; - node.parent = replacement; - } - - protected insertNode(Node: typeof BSNode, value: T): BSNode | null { - if (!this.root) { - this.root = new Node(null, value); - this._size++; - return this.root; - } else { - let node: BSNode = this.root; - while (true) { - const order: number = this.compare(value, node.value); - if (order === 0) break; - const direction: direction = order < 0 ? "left" : "right"; - if (node[direction]) { - node = node[direction]!; - } else { - node[direction] = new Node(node, value); - this._size++; - return node[direction]; - } - } - } - return null; - } - - protected removeNode( - value: T, - ): BSNode | null { - let removeNode: BSNode | null = this.findNode(value); - if (removeNode) { - const successorNode: BSNode | null = - !removeNode.left || !removeNode.right - ? removeNode - : removeNode.findSuccessorNode()!; - const replacementNode: BSNode | null = successorNode.left ?? - successorNode.right; - if (replacementNode) replacementNode.parent = successorNode.parent; - - if (!successorNode.parent) { - this.root = replacementNode; - } else { - successorNode.parent[successorNode.directionFromParent()!] = - replacementNode; - } - - if (successorNode !== removeNode) { - removeNode.value = successorNode.value; - removeNode = successorNode; - } - this._size--; - } - return removeNode; - } - - /** - * Adds the value to the binary search tree if it does not already exist in it. - * Returns true if successful. - */ - insert(value: T): boolean { - return !!this.insertNode(BSNode, value); - } - - /** - * Removes node value from the binary search tree if found. - * Returns true if found and removed. - */ - remove(value: T): boolean { - return !!this.removeNode(value); - } - - /** Returns node value if found in the binary search tree. */ - find(value: T): T | null { - return this.findNode(value)?.value ?? null; - } - - /** Returns the minimum value in the binary search tree or null if 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. */ - max(): T | null { - return this.root ? this.root.findMaxNode().value : null; - } - - /** Removes all values from the binary search tree. */ - clear(): void { - this.root = null; - this._size = 0; - } - - /** Checks if the binary search tree is empty. */ - isEmpty(): boolean { - return this.size === 0; - } - - /** - * Returns an iterator that uses in-order (LNR) tree traversal for - * retrieving values from the binary search tree. - */ - *lnrValues(): IterableIterator { - const nodes: BSNode[] = []; - let node: BSNode | null = this.root; - while (nodes.length || node) { - if (node) { - nodes.push(node); - node = node.left; - } else { - node = nodes.pop()!; - yield node.value; - node = node.right; - } - } - } - - /** - * Returns an iterator that uses reverse in-order (RNL) tree traversal for - * retrieving values from the binary search tree. - */ - *rnlValues(): IterableIterator { - const nodes: BSNode[] = []; - let node: BSNode | null = this.root; - while (nodes.length || node) { - if (node) { - nodes.push(node); - node = node.right; - } else { - node = nodes.pop()!; - yield node.value; - node = node.left; - } - } - } - - /** - * Returns an iterator that uses pre-order (NLR) tree traversal for - * retrieving values from the binary search tree. - */ - *nlrValues(): IterableIterator { - const nodes: BSNode[] = []; - if (this.root) nodes.push(this.root); - while (nodes.length) { - const node: BSNode = nodes.pop()!; - yield node.value; - if (node.right) nodes.push(node.right); - if (node.left) nodes.push(node.left); - } - } - - /** - * Returns an iterator that uses post-order (LRN) tree traversal for - * retrieving values from the binary search tree. - */ - *lrnValues(): IterableIterator { - const nodes: BSNode[] = []; - let node: BSNode | null = this.root; - let lastNodeVisited: BSNode | null = null; - while (nodes.length || node) { - if (node) { - nodes.push(node); - node = node.left; - } else { - const lastNode: BSNode = nodes[nodes.length - 1]; - if (lastNode.right && lastNode.right !== lastNodeVisited) { - node = lastNode.right; - } else { - yield lastNode.value; - lastNodeVisited = nodes.pop()!; - } - } - } - } - - /** - * Returns an iterator that uses level order tree traversal for - * retrieving values from the binary search tree. - */ - *lvlValues(): IterableIterator { - const children: BSNode[] = []; - let cursor: BSNode | null = this.root; - while (cursor) { - yield cursor.value; - if (cursor.left) children.push(cursor.left); - if (cursor.right) children.push(cursor.right); - cursor = children.shift() ?? null; - } - } - - /** - * Returns an iterator that uses in-order (LNR) tree traversal for - * retrieving values from the binary search tree. - */ - *[Symbol.iterator](): IterableIterator { - yield* this.lnrValues(); - } -} +/** @deprecated use BinarySearchTree instead */ +export { BinarySearchTree as BSTree }; diff --git a/collections/rb_node.ts b/collections/rb_node.ts index 65fe5e9d3f55..4619e3485b3f 100644 --- a/collections/rb_node.ts +++ b/collections/rb_node.ts @@ -1,25 +1,13 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. /** This module is browser compatible. */ -import { BSNode, direction } from "./bs_node.ts"; -export type { direction }; +import { Direction } from "./bs_node.ts"; +import { RedBlackNode } from "./red_black_node.ts"; -export class RBNode extends BSNode { - declare parent: RBNode | null; - declare left: RBNode | null; - declare right: RBNode | null; - red: boolean; +/** @deprecated use Direction instead */ +export type { Direction as direction }; - constructor(parent: RBNode | null, value: T) { - super(parent, value); - this.red = true; - } +export type { Direction }; - static override from(node: RBNode): RBNode { - const copy: RBNode = new RBNode(node.parent, node.value); - copy.left = node.left; - copy.right = node.right; - copy.red = node.red; - return copy; - } -} +/** @deprecated use RedBlackNode instead */ +export { RedBlackNode as RBNode }; diff --git a/collections/rb_tree.ts b/collections/rb_tree.ts index 945eace3784b..a471bc085532 100644 --- a/collections/rb_tree.ts +++ b/collections/rb_tree.ts @@ -1,180 +1,8 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. /** This module is browser compatible. */ -import { ascend, BSTree } from "./bs_tree.ts"; -import { direction, RBNode } from "./rb_node.ts"; +import { RedBlackTree } from "./red_black_tree.ts"; export * from "./_comparators.ts"; -/** - * A red-black tree. This is a kind of self-balancing binary search tree. - * The values are in ascending order by default, - * using JavaScript's built in comparison operators to sort the values. - */ -export class RBTree extends BSTree { - declare protected root: RBNode | null; - - 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 | RBTree, - ): RBTree; - static override from( - collection: ArrayLike | Iterable | RBTree, - options: { - Node?: typeof RBNode; - compare?: (a: T, b: T) => number; - }, - ): RBTree; - static override from( - collection: ArrayLike | Iterable | RBTree, - options: { - compare?: (a: U, b: U) => number; - map: (value: T, index: number) => U; - thisArg?: V; - }, - ): RBTree; - static override from( - collection: ArrayLike | Iterable | RBTree, - options?: { - compare?: (a: U, b: U) => number; - map?: (value: T, index: number) => U; - thisArg?: V; - }, - ): RBTree { - let result: RBTree; - let unmappedValues: ArrayLike | Iterable = []; - if (collection instanceof RBTree) { - result = new RBTree( - options?.compare ?? (collection as unknown as RBTree).compare, - ); - if (options?.compare || options?.map) { - unmappedValues = collection; - } else { - const nodes: RBNode[] = []; - if (collection.root) { - result.root = RBNode.from(collection.root as unknown as RBNode); - nodes.push(result.root); - } - while (nodes.length) { - const node: RBNode = nodes.pop()!; - const left: RBNode | null = node.left - ? RBNode.from(node.left) - : null; - const right: RBNode | null = node.right - ? RBNode.from(node.right) - : null; - - if (left) { - left.parent = node; - nodes.push(left); - } - if (right) { - right.parent = node; - nodes.push(right); - } - } - } - } else { - result = (options?.compare - ? new RBTree(options.compare) - : new RBTree()) as RBTree; - unmappedValues = collection; - } - const values: Iterable = options?.map - ? Array.from(unmappedValues, options.map, options.thisArg) - : unmappedValues as U[]; - for (const value of values) result.insert(value); - return result; - } - - protected removeFixup(parent: RBNode | null, current: RBNode | null) { - while (parent && !current?.red) { - const direction: direction = parent.left === current ? "left" : "right"; - const siblingDirection: direction = direction === "right" - ? "left" - : "right"; - let sibling: RBNode | null = parent[siblingDirection]; - - if (sibling?.red) { - sibling.red = false; - parent.red = true; - this.rotateNode(parent, direction); - sibling = parent[siblingDirection]; - } - if (sibling) { - if (!sibling.left?.red && !sibling.right?.red) { - sibling!.red = true; - current = parent; - parent = current.parent; - } else { - if (!sibling[siblingDirection]?.red) { - sibling[direction]!.red = false; - sibling.red = true; - this.rotateNode(sibling, siblingDirection); - sibling = parent[siblingDirection!]; - } - sibling!.red = parent.red; - parent.red = false; - sibling![siblingDirection]!.red = false; - this.rotateNode(parent, direction); - current = this.root; - parent = null; - } - } - } - if (current) current.red = false; - } - - /** - * Adds the value to the binary search tree if it does not already exist in it. - * Returns true if successful. - */ - override insert(value: T): boolean { - let node = this.insertNode(RBNode, value) as (RBNode | null); - if (node) { - while (node.parent?.red) { - let parent: RBNode = node.parent!; - const parentDirection: direction = parent.directionFromParent()!; - const uncleDirection: direction = parentDirection === "right" - ? "left" - : "right"; - const uncle: RBNode | null = parent.parent![uncleDirection] ?? null; - - if (uncle?.red) { - parent.red = false; - uncle.red = false; - parent.parent!.red = true; - node = parent.parent!; - } else { - if (node === parent[uncleDirection]) { - node = parent; - this.rotateNode(node, parentDirection); - parent = node.parent!; - } - parent.red = false; - parent.parent!.red = true; - this.rotateNode(parent.parent!, uncleDirection); - } - } - this.root!.red = false; - } - return !!node; - } - - /** - * Removes node value from the binary search tree if found. - * Returns true if found and removed. - */ - override remove(value: T): boolean { - const node = this.removeNode(value) as (RBNode | null); - if (node && !node.red) { - this.removeFixup(node.parent, node.left ?? node.right); - } - return !!node; - } -} +/** @deprecated use RedBlackTree instead */ +export { RedBlackTree as RBTree }; diff --git a/collections/red_black_node.ts b/collections/red_black_node.ts new file mode 100644 index 000000000000..e4966c528e78 --- /dev/null +++ b/collections/red_black_node.ts @@ -0,0 +1,30 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +/** This module is browser compatible. */ + +import { BinarySearchNode, Direction } from "./binary_search_node.ts"; +export type { Direction }; + +/** + * @deprecated use Direction instead + */ +export type { Direction as direction }; + +export class RedBlackNode extends BinarySearchNode { + declare parent: RedBlackNode | null; + declare left: RedBlackNode | null; + declare right: RedBlackNode | null; + red: boolean; + + constructor(parent: RedBlackNode | null, value: T) { + super(parent, value); + this.red = true; + } + + static override from(node: RedBlackNode): RedBlackNode { + const copy: RedBlackNode = new RedBlackNode(node.parent, node.value); + copy.left = node.left; + copy.right = node.right; + copy.red = node.red; + return copy; + } +} diff --git a/collections/rb_node_test.ts b/collections/red_black_node_test.ts similarity index 73% rename from collections/rb_node_test.ts rename to collections/red_black_node_test.ts index b110c7963e64..8c19de7703f4 100644 --- a/collections/rb_node_test.ts +++ b/collections/red_black_node_test.ts @@ -1,15 +1,15 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. import { assertStrictEquals } from "../testing/asserts.ts"; -import { RBNode } from "./rb_node.ts"; +import { RedBlackNode } from "./red_black_node.ts"; -Deno.test("[collections/RBNode] constructor and from", () => { - const parent: RBNode = new RBNode(null, 5); - const child: RBNode = new RBNode(parent, 7); +Deno.test("[collections/RedBlackNode] constructor and from", () => { + const parent: RedBlackNode = new RedBlackNode(null, 5); + const child: RedBlackNode = new RedBlackNode(parent, 7); parent.left = child; assertStrictEquals(parent.red, true); parent.red = false; - const parentClone: RBNode = RBNode.from(parent); - const childClone: RBNode = RBNode.from(child); + const parentClone: RedBlackNode = RedBlackNode.from(parent); + const childClone: RedBlackNode = RedBlackNode.from(child); assertStrictEquals(parent.parent, null); assertStrictEquals(parent.left, child); diff --git a/collections/red_black_tree.ts b/collections/red_black_tree.ts new file mode 100644 index 000000000000..6a200a45d949 --- /dev/null +++ b/collections/red_black_tree.ts @@ -0,0 +1,186 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +/** This module is browser compatible. */ + +import { ascend, BinarySearchTree } from "./binary_search_tree.ts"; +import { Direction, RedBlackNode } from "./red_black_node.ts"; +export * from "./_comparators.ts"; + +/** + * A red-black tree. This is a kind of self-balancing binary search tree. + * The values are in ascending order by default, + * using JavaScript's built in comparison operators to sort the values. + */ +export class RedBlackTree extends BinarySearchTree { + declare protected root: RedBlackNode | null; + + 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; + static override from( + collection: ArrayLike | Iterable | RedBlackTree, + options: { + Node?: typeof RedBlackNode; + compare?: (a: T, b: T) => number; + }, + ): RedBlackTree; + static override from( + collection: ArrayLike | Iterable | RedBlackTree, + options: { + compare?: (a: U, b: U) => number; + map: (value: T, index: number) => U; + thisArg?: V; + }, + ): RedBlackTree; + static override from( + collection: ArrayLike | Iterable | RedBlackTree, + options?: { + compare?: (a: U, b: U) => number; + map?: (value: T, index: number) => U; + thisArg?: V; + }, + ): RedBlackTree { + let result: RedBlackTree; + let unmappedValues: ArrayLike | Iterable = []; + if (collection instanceof RedBlackTree) { + result = new RedBlackTree( + options?.compare ?? (collection as unknown as RedBlackTree).compare, + ); + if (options?.compare || options?.map) { + unmappedValues = collection; + } else { + const nodes: RedBlackNode[] = []; + if (collection.root) { + result.root = RedBlackNode.from( + collection.root as unknown as RedBlackNode, + ); + nodes.push(result.root); + } + while (nodes.length) { + const node: RedBlackNode = nodes.pop()!; + const left: RedBlackNode | null = node.left + ? RedBlackNode.from(node.left) + : null; + const right: RedBlackNode | null = node.right + ? RedBlackNode.from(node.right) + : null; + + if (left) { + left.parent = node; + nodes.push(left); + } + if (right) { + right.parent = node; + nodes.push(right); + } + } + } + } else { + result = (options?.compare + ? new RedBlackTree(options.compare) + : new RedBlackTree()) as RedBlackTree; + unmappedValues = collection; + } + const values: Iterable = options?.map + ? Array.from(unmappedValues, options.map, options.thisArg) + : unmappedValues as U[]; + for (const value of values) result.insert(value); + return result; + } + + protected removeFixup( + parent: RedBlackNode | null, + current: RedBlackNode | null, + ) { + while (parent && !current?.red) { + const direction: Direction = parent.left === current ? "left" : "right"; + const siblingDirection: Direction = direction === "right" + ? "left" + : "right"; + let sibling: RedBlackNode | null = parent[siblingDirection]; + + if (sibling?.red) { + sibling.red = false; + parent.red = true; + this.rotateNode(parent, direction); + sibling = parent[siblingDirection]; + } + if (sibling) { + if (!sibling.left?.red && !sibling.right?.red) { + sibling!.red = true; + current = parent; + parent = current.parent; + } else { + if (!sibling[siblingDirection]?.red) { + sibling[direction]!.red = false; + sibling.red = true; + this.rotateNode(sibling, siblingDirection); + sibling = parent[siblingDirection!]; + } + sibling!.red = parent.red; + parent.red = false; + sibling![siblingDirection]!.red = false; + this.rotateNode(parent, direction); + current = this.root; + parent = null; + } + } + } + if (current) current.red = false; + } + + /** + * Adds the value to the binary search tree if it does not already exist in it. + * Returns true if successful. + */ + override insert(value: T): boolean { + let node = this.insertNode(RedBlackNode, value) as (RedBlackNode | null); + if (node) { + while (node.parent?.red) { + let parent: RedBlackNode = node.parent!; + const parentDirection: Direction = parent.directionFromParent()!; + const uncleDirection: Direction = parentDirection === "right" + ? "left" + : "right"; + const uncle: RedBlackNode | null = parent.parent![uncleDirection] ?? + null; + + if (uncle?.red) { + parent.red = false; + uncle.red = false; + parent.parent!.red = true; + node = parent.parent!; + } else { + if (node === parent[uncleDirection]) { + node = parent; + this.rotateNode(node, parentDirection); + parent = node.parent!; + } + parent.red = false; + parent.parent!.red = true; + this.rotateNode(parent.parent!, uncleDirection); + } + } + this.root!.red = false; + } + return !!node; + } + + /** + * Removes node value from the binary search tree if found. + * Returns true if found and removed. + */ + override remove(value: T): boolean { + const node = this.removeNode(value) as (RedBlackNode | null); + if (node && !node.red) { + this.removeFixup(node.parent, node.left ?? node.right); + } + return !!node; + } +} diff --git a/collections/rb_tree_test.ts b/collections/red_black_tree_test.ts similarity index 85% rename from collections/rb_tree_test.ts rename to collections/red_black_tree_test.ts index 23e53d63ebce..1f9dc4c04d9d 100644 --- a/collections/rb_tree_test.ts +++ b/collections/red_black_tree_test.ts @@ -1,10 +1,13 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. import { assertEquals, assertStrictEquals } from "../testing/asserts.ts"; -import { ascend, descend, RBTree } from "./rb_tree.ts"; +import { ascend, descend, RedBlackTree } from "./red_black_tree.ts"; import { Container, MyMath } from "./_test_utils.ts"; -Deno.test("[collections/RBTree] with default ascend comparator", () => { - const trees: RBTree[] = [new RBTree(), new RBTree()]; +Deno.test("[collections/RedBlackTree] with default ascend comparator", () => { + const trees: RedBlackTree[] = [ + new RedBlackTree(), + new RedBlackTree(), + ]; const values: number[] = [-10, 9, -1, 100, 1, 0, -100, 10, -9]; const expectedMin: number[][] = [ @@ -120,8 +123,11 @@ Deno.test("[collections/RBTree] with default ascend comparator", () => { } }); -Deno.test("[collections/RBTree] with descend comparator", () => { - const trees: RBTree[] = [new RBTree(descend), new RBTree(descend)]; +Deno.test("[collections/RedBlackTree] with descend comparator", () => { + const trees: RedBlackTree[] = [ + new RedBlackTree(descend), + new RedBlackTree(descend), + ]; const values: number[] = [-10, 9, -1, 100, 1, 0, -100, 10, -9]; const expectedMin: number[][] = [ @@ -237,8 +243,8 @@ Deno.test("[collections/RBTree] with descend comparator", () => { } }); -Deno.test("[collections/RBTree] containing objects", () => { - const tree: RBTree = new RBTree(( +Deno.test("[collections/RedBlackTree] containing objects", () => { + const tree: RedBlackTree = new RedBlackTree(( a: Container, b: Container, ) => ascend(a.id, b.id)); @@ -297,23 +303,23 @@ Deno.test("[collections/RBTree] containing objects", () => { assertEquals(tree.isEmpty(), true); }); -Deno.test("[collections/RBTree] from Iterable", () => { +Deno.test("[collections/RedBlackTree] from Iterable", () => { const values: number[] = [-10, 9, -1, 100, 9, 1, 0, 9, -100, 10, -9]; const originalValues: number[] = Array.from(values); const expected: number[] = [-100, -10, -9, -1, 0, 1, 9, 10, 100]; - let tree: RBTree = RBTree.from(values); + let tree: RedBlackTree = RedBlackTree.from(values); assertEquals(values, originalValues); assertEquals([...tree], expected); assertEquals([...tree.nlrValues()], [-1, -10, -100, -9, 9, 1, 0, 100, 10]); assertEquals([...tree.lvlValues()], [-1, -10, 9, -100, -9, 1, 100, 0, 10]); - tree = RBTree.from(values, { compare: descend }); + tree = RedBlackTree.from(values, { compare: descend }); assertEquals(values, originalValues); assertEquals([...tree].reverse(), expected); assertEquals([...tree.nlrValues()], [-1, 9, 100, 10, 1, 0, -10, -9, -100]); assertEquals([...tree.lvlValues()], [-1, 9, -10, 100, 1, -9, -100, 10, 0]); - tree = RBTree.from(values, { + tree = RedBlackTree.from(values, { map: (v: number) => 2 * v, }); assertEquals([...tree], expected.map((v: number) => 2 * v)); @@ -321,7 +327,7 @@ Deno.test("[collections/RBTree] from Iterable", () => { assertEquals([...tree.lvlValues()], [-2, -20, 18, -200, -18, 2, 200, 0, 20]); const math = new MyMath(); - tree = RBTree.from(values, { + tree = RedBlackTree.from(values, { map: function (this: MyMath, v: number) { return this.multiply(3, v); }, @@ -332,7 +338,7 @@ Deno.test("[collections/RBTree] from Iterable", () => { assertEquals([...tree.nlrValues()], [-3, -30, -300, -27, 27, 3, 0, 300, 30]); assertEquals([...tree.lvlValues()], [-3, -30, 27, -300, -27, 3, 300, 0, 30]); - tree = RBTree.from(values, { + tree = RedBlackTree.from(values, { compare: descend, map: (v: number) => 2 * v, }); @@ -341,7 +347,7 @@ Deno.test("[collections/RBTree] from Iterable", () => { assertEquals([...tree.nlrValues()], [-2, 18, 200, 20, 2, 0, -20, -18, -200]); assertEquals([...tree.lvlValues()], [-2, 18, -20, 200, 2, -18, -200, 20, 0]); - tree = RBTree.from(values, { + tree = RedBlackTree.from(values, { compare: descend, map: function (this: MyMath, v: number) { return this.multiply(3, v); @@ -354,24 +360,24 @@ Deno.test("[collections/RBTree] from Iterable", () => { assertEquals([...tree.lvlValues()], [-3, 27, -30, 300, 3, -27, -300, 30, 0]); }); -Deno.test("[collections/RBTree] from RBTree with default ascend comparator", () => { +Deno.test("[collections/RedBlackTree] from RedBlackTree with default ascend comparator", () => { const values: number[] = [-10, 9, -1, 100, 9, 1, 0, 9, -100, 10, -9]; const expected: number[] = [-100, -10, -9, -1, 0, 1, 9, 10, 100]; - const originalTree: RBTree = new RBTree(); + const originalTree: RedBlackTree = new RedBlackTree(); for (const value of values) originalTree.insert(value); - let tree: RBTree = RBTree.from(originalTree); + let tree: RedBlackTree = RedBlackTree.from(originalTree); assertEquals([...originalTree], expected); assertEquals([...tree], expected); assertEquals([...tree.nlrValues()], [...originalTree.nlrValues()]); assertEquals([...tree.lvlValues()], [...originalTree.lvlValues()]); - tree = RBTree.from(originalTree, { compare: descend }); + tree = RedBlackTree.from(originalTree, { compare: descend }); assertEquals([...originalTree], expected); assertEquals([...tree].reverse(), expected); assertEquals([...tree.nlrValues()], [-1, 1, 10, 100, 9, 0, -10, -9, -100]); assertEquals([...tree.lvlValues()], [-1, 1, -10, 10, 0, -9, -100, 100, 9]); - tree = RBTree.from(originalTree, { + tree = RedBlackTree.from(originalTree, { map: (v: number) => 2 * v, }); assertEquals([...originalTree], expected); @@ -380,7 +386,7 @@ Deno.test("[collections/RBTree] from RBTree with default ascend comparator", () assertEquals([...tree.lvlValues()], [-2, -20, 2, -200, -18, 0, 20, 18, 200]); const math = new MyMath(); - tree = RBTree.from(originalTree, { + tree = RedBlackTree.from(originalTree, { map: function (this: MyMath, v: number) { return this.multiply(3, v); }, @@ -391,7 +397,7 @@ Deno.test("[collections/RBTree] from RBTree with default ascend comparator", () assertEquals([...tree.nlrValues()], [-3, -30, -300, -27, 3, 0, 30, 27, 300]); assertEquals([...tree.lvlValues()], [-3, -30, 3, -300, -27, 0, 30, 27, 300]); - tree = RBTree.from(originalTree, { + tree = RedBlackTree.from(originalTree, { compare: descend, map: (v: number) => 2 * v, }); @@ -400,7 +406,7 @@ Deno.test("[collections/RBTree] from RBTree with default ascend comparator", () assertEquals([...tree.nlrValues()], [-2, 2, 20, 200, 18, 0, -20, -18, -200]); assertEquals([...tree.lvlValues()], [-2, 2, -20, 20, 0, -18, -200, 200, 18]); - tree = RBTree.from(originalTree, { + tree = RedBlackTree.from(originalTree, { compare: descend, map: function (this: MyMath, v: number) { return this.multiply(3, v); @@ -413,24 +419,24 @@ Deno.test("[collections/RBTree] from RBTree with default ascend comparator", () assertEquals([...tree.lvlValues()], [-3, 3, -30, 30, 0, -27, -300, 300, 27]); }); -Deno.test("[collections/RBTree] from RBTree with descend comparator", () => { +Deno.test("[collections/RedBlackTree] from RedBlackTree with descend comparator", () => { const values: number[] = [-10, 9, -1, 100, 9, 1, 0, 9, -100, 10, -9]; const expected: number[] = [100, 10, 9, 1, 0, -1, -9, -10, -100]; - const originalTree: RBTree = new RBTree(descend); + const originalTree: RedBlackTree = new RedBlackTree(descend); for (const value of values) originalTree.insert(value); - let tree: RBTree = RBTree.from(originalTree); + let tree: RedBlackTree = RedBlackTree.from(originalTree); assertEquals([...originalTree], expected); assertEquals([...tree], expected); assertEquals([...tree.nlrValues()], [...originalTree.nlrValues()]); assertEquals([...tree.lvlValues()], [...originalTree.lvlValues()]); - tree = RBTree.from(originalTree, { compare: ascend }); + tree = RedBlackTree.from(originalTree, { compare: ascend }); assertEquals([...originalTree], expected); assertEquals([...tree].reverse(), expected); assertEquals([...tree.nlrValues()], [1, -1, -10, -100, -9, 0, 10, 9, 100]); assertEquals([...tree.lvlValues()], [1, -1, 10, -10, 0, 9, 100, -100, -9]); - tree = RBTree.from(originalTree, { + tree = RedBlackTree.from(originalTree, { map: (v: number) => 2 * v, }); assertEquals([...originalTree], expected); @@ -439,7 +445,7 @@ Deno.test("[collections/RBTree] from RBTree with descend comparator", () => { assertEquals([...tree.lvlValues()], [2, 20, -2, 200, 18, 0, -20, -18, -200]); const math = new MyMath(); - tree = RBTree.from(originalTree, { + tree = RedBlackTree.from(originalTree, { map: function (this: MyMath, v: number) { return this.multiply(3, v); }, @@ -450,7 +456,7 @@ Deno.test("[collections/RBTree] from RBTree with descend comparator", () => { assertEquals([...tree.nlrValues()], [3, 30, 300, 27, -3, 0, -30, -27, -300]); assertEquals([...tree.lvlValues()], [3, 30, -3, 300, 27, 0, -30, -27, -300]); - tree = RBTree.from(originalTree, { + tree = RedBlackTree.from(originalTree, { compare: ascend, map: (v: number) => 2 * v, }); @@ -459,7 +465,7 @@ Deno.test("[collections/RBTree] from RBTree with descend comparator", () => { assertEquals([...tree.nlrValues()], [2, -2, -20, -200, -18, 0, 20, 18, 200]); assertEquals([...tree.lvlValues()], [2, -2, 20, -20, 0, 18, 200, -200, -18]); - tree = RBTree.from(originalTree, { + tree = RedBlackTree.from(originalTree, { compare: ascend, map: function (this: MyMath, v: number) { return this.multiply(3, v); @@ -472,223 +478,223 @@ Deno.test("[collections/RBTree] from RBTree with descend comparator", () => { assertEquals([...tree.lvlValues()], [3, -3, 30, -30, 0, 27, 300, -300, -27]); }); -Deno.test("[collections/RBTree] insert rebalance left", () => { +Deno.test("[collections/RedBlackTree] insert rebalance left", () => { let values: number[] = [8, 4, 10, 0, 6, 11, -2, 2]; - let tree: RBTree = RBTree.from(values); + let tree: RedBlackTree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [8, 4, 0, -2, 2, 6, 10, 11]); assertEquals([...tree.lvlValues()], [8, 4, 10, 0, 6, 11, -2, 2]); assertEquals(tree.insert(-3), true); assertEquals([...tree.nlrValues()], [4, 0, -2, -3, 2, 8, 6, 10, 11]); assertEquals([...tree.lvlValues()], [4, 0, 8, -2, 2, 6, 10, -3, 11]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(-1), true); assertEquals([...tree.nlrValues()], [4, 0, -2, -1, 2, 8, 6, 10, 11]); assertEquals([...tree.lvlValues()], [4, 0, 8, -2, 2, 6, 10, -1, 11]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(1), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 1, 8, 6, 10, 11]); assertEquals([...tree.lvlValues()], [4, 0, 8, -2, 2, 6, 10, 1, 11]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(3), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 3, 8, 6, 10, 11]); assertEquals([...tree.lvlValues()], [4, 0, 8, -2, 2, 6, 10, 3, 11]); values = [4, -4, 6, -5, 0, 7, -2, 2]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, -4, -5, 0, -2, 2, 6, 7]); assertEquals([...tree.lvlValues()], [4, -4, 6, -5, 0, 7, -2, 2]); assertEquals(tree.insert(-3), true); assertEquals([...tree.nlrValues()], [0, -4, -5, -2, -3, 4, 2, 6, 7]); assertEquals([...tree.lvlValues()], [0, -4, 4, -5, -2, 2, 6, -3, 7]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(-1), true); assertEquals([...tree.nlrValues()], [0, -4, -5, -2, -1, 4, 2, 6, 7]); assertEquals([...tree.lvlValues()], [0, -4, 4, -5, -2, 2, 6, -1, 7]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(1), true); assertEquals([...tree.nlrValues()], [0, -4, -5, -2, 4, 2, 1, 6, 7]); assertEquals([...tree.lvlValues()], [0, -4, 4, -5, -2, 2, 6, 1, 7]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(3), true); assertEquals([...tree.nlrValues()], [0, -4, -5, -2, 4, 2, 3, 6, 7]); assertEquals([...tree.lvlValues()], [0, -4, 4, -5, -2, 2, 6, 3, 7]); }); -Deno.test("[collections/RBTree] insert rebalance right", () => { +Deno.test("[collections/RedBlackTree] insert rebalance right", () => { let values: number[] = [-4, -6, 4, 0, 6, -7, -2, 2]; - let tree: RBTree = RBTree.from(values); + let tree: RedBlackTree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -6, -7, 4, 0, -2, 2, 6]); assertEquals([...tree.lvlValues()], [-4, -6, 4, -7, 0, 6, -2, 2]); assertEquals(tree.insert(-3), true); assertEquals([...tree.nlrValues()], [0, -4, -6, -7, -2, -3, 4, 2, 6]); assertEquals([...tree.lvlValues()], [0, -4, 4, -6, -2, 2, 6, -7, -3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(-1), true); assertEquals([...tree.nlrValues()], [0, -4, -6, -7, -2, -1, 4, 2, 6]); assertEquals([...tree.lvlValues()], [0, -4, 4, -6, -2, 2, 6, -7, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(1), true); assertEquals([...tree.nlrValues()], [0, -4, -6, -7, -2, 4, 2, 1, 6]); assertEquals([...tree.lvlValues()], [0, -4, 4, -6, -2, 2, 6, -7, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(3), true); assertEquals([...tree.nlrValues()], [0, -4, -6, -7, -2, 4, 2, 3, 6]); assertEquals([...tree.lvlValues()], [0, -4, 4, -6, -2, 2, 6, -7, 3]); values = [-8, -10, -4, -11, -6, 0, -2, 2]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-8, -10, -11, -4, -6, 0, -2, 2]); assertEquals([...tree.lvlValues()], [-8, -10, -4, -11, -6, 0, -2, 2]); assertEquals(tree.insert(-3), true); assertEquals([...tree.nlrValues()], [-4, -8, -10, -11, -6, 0, -2, -3, 2]); assertEquals([...tree.lvlValues()], [-4, -8, 0, -10, -6, -2, 2, -11, -3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(-1), true); assertEquals([...tree.nlrValues()], [-4, -8, -10, -11, -6, 0, -2, -1, 2]); assertEquals([...tree.lvlValues()], [-4, -8, 0, -10, -6, -2, 2, -11, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(1), true); assertEquals([...tree.nlrValues()], [-4, -8, -10, -11, -6, 0, -2, 2, 1]); assertEquals([...tree.lvlValues()], [-4, -8, 0, -10, -6, -2, 2, -11, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.insert(3), true); assertEquals([...tree.nlrValues()], [-4, -8, -10, -11, -6, 0, -2, 2, 3]); assertEquals([...tree.lvlValues()], [-4, -8, 0, -10, -6, -2, 2, -11, 3]); }); -Deno.test("[collections/RBTree] remove rebalance root", () => { +Deno.test("[collections/RedBlackTree] remove rebalance root", () => { let values: number[] = [0]; - let tree: RBTree = RBTree.from(values); + let tree: RedBlackTree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [0]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], []); values = [0, -1, 1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [0, -1, 1]); assertEquals([...tree.lvlValues()], [0, -1, 1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [1, -1]); assertEquals([...tree.lvlValues()], [1, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [0, 1]); assertEquals([...tree.lvlValues()], [0, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [0, -1]); assertEquals([...tree.lvlValues()], [0, -1]); values = [0, -2, 2, -3, -1, 1, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [0, -2, -3, -1, 2, 1, 3]); assertEquals([...tree.lvlValues()], [0, -2, 2, -3, -1, 1, 3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [1, -2, -3, -1, 2, 3]); assertEquals([...tree.lvlValues()], [1, -2, 2, -3, -1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [0, -1, -3, 2, 1, 3]); assertEquals([...tree.lvlValues()], [0, -1, 2, -3, 1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [0, -2, -3, -1, 3, 1]); assertEquals([...tree.lvlValues()], [0, -2, 3, -3, -1, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals([...tree.nlrValues()], [0, -2, -1, 2, 1, 3]); assertEquals([...tree.lvlValues()], [0, -2, 2, -1, 1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [0, -2, -3, 2, 1, 3]); assertEquals([...tree.lvlValues()], [0, -2, 2, -3, 1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [0, -2, -3, -1, 2, 3]); assertEquals([...tree.lvlValues()], [0, -2, 2, -3, -1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals([...tree.nlrValues()], [0, -2, -3, -1, 2, 1]); assertEquals([...tree.lvlValues()], [0, -2, 2, -3, -1, 1]); values = [0, -2, 2, -3, -1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [0, -2, -3, -1, 2]); assertEquals([...tree.lvlValues()], [0, -2, 2, -3, -1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-2, -3, 2, -1]); assertEquals([...tree.lvlValues()], [-2, -3, 2, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [0, -1, -3, 2]); assertEquals([...tree.lvlValues()], [0, -1, 2, -3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-2, -3, 0, -1]); assertEquals([...tree.lvlValues()], [-2, -3, 0, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals([...tree.nlrValues()], [0, -2, -1, 2]); assertEquals([...tree.lvlValues()], [0, -2, 2, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [0, -2, -3, 2]); assertEquals([...tree.lvlValues()], [0, -2, 2, -3]); values = [0, -2, 2, 1, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [0, -2, 2, 1, 3]); assertEquals([...tree.lvlValues()], [0, -2, 2, 1, 3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [1, -2, 2, 3]); assertEquals([...tree.lvlValues()], [1, -2, 2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [2, 0, 1, 3]); assertEquals([...tree.lvlValues()], [2, 0, 3, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [0, -2, 3, 1]); assertEquals([...tree.lvlValues()], [0, -2, 3, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [0, -2, 2, 3]); assertEquals([...tree.lvlValues()], [0, -2, 2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals([...tree.nlrValues()], [0, -2, 2, 1]); assertEquals([...tree.lvlValues()], [0, -2, 2, 1]); values = [0, -2, 2, -3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [0, -2, -3, 2]); assertEquals([...tree.lvlValues()], [0, -2, 2, -3]); assertEquals(tree.remove(-3), true); @@ -698,20 +704,20 @@ Deno.test("[collections/RBTree] remove rebalance root", () => { assertEquals([...tree.nlrValues()], [2, -2]); assertEquals([...tree.lvlValues()], [2, -2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [0, 2]); assertEquals([...tree.lvlValues()], [0, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [0, -2]); assertEquals([...tree.lvlValues()], [0, -2]); values = [0, -2, 2, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [0, -2, 2, 3]); assertEquals([...tree.lvlValues()], [0, -2, 2, 3]); assertEquals(tree.remove(3), true); @@ -721,194 +727,194 @@ Deno.test("[collections/RBTree] remove rebalance root", () => { assertEquals([...tree.nlrValues()], [2, -2]); assertEquals([...tree.lvlValues()], [2, -2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [0, 2]); assertEquals([...tree.lvlValues()], [0, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [0, -2]); assertEquals([...tree.lvlValues()], [0, -2]); values = [0, -2, 2, -3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [0, -2, -3, 2]); assertEquals([...tree.lvlValues()], [0, -2, 2, -3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-2, -3, 2]); assertEquals([...tree.lvlValues()], [-2, -3, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [0, -3, 2]); assertEquals([...tree.lvlValues()], [0, -3, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-2, -3, 0]); assertEquals([...tree.lvlValues()], [-2, -3, 0]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals([...tree.nlrValues()], [0, -2, 2]); assertEquals([...tree.lvlValues()], [0, -2, 2]); values = [0, -2, 2, -1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [0, -2, -1, 2]); assertEquals([...tree.lvlValues()], [0, -2, 2, -1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-1, -2, 2]); assertEquals([...tree.lvlValues()], [-1, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [0, -1, 2]); assertEquals([...tree.lvlValues()], [0, -1, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-1, -2, 0]); assertEquals([...tree.lvlValues()], [-1, -2, 0]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [0, -2, 2]); assertEquals([...tree.lvlValues()], [0, -2, 2]); values = [0, -2, 2, 1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [0, -2, 2, 1]); assertEquals([...tree.lvlValues()], [0, -2, 2, 1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [1, -2, 2]); assertEquals([...tree.lvlValues()], [1, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [1, 0, 2]); assertEquals([...tree.lvlValues()], [1, 0, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [0, -2, 1]); assertEquals([...tree.lvlValues()], [0, -2, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [0, -2, 2]); assertEquals([...tree.lvlValues()], [0, -2, 2]); values = [0, -2, 2, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [0, -2, 2, 3]); assertEquals([...tree.lvlValues()], [0, -2, 2, 3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [2, -2, 3]); assertEquals([...tree.lvlValues()], [2, -2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [2, 0, 3]); assertEquals([...tree.lvlValues()], [2, 0, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [0, -2, 3]); assertEquals([...tree.lvlValues()], [0, -2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals([...tree.nlrValues()], [0, -2, 2]); assertEquals([...tree.lvlValues()], [0, -2, 2]); }); -Deno.test("[collections/RBTree] remove rebalance left", () => { +Deno.test("[collections/RedBlackTree] remove rebalance left", () => { let values = [4, 5, 0]; - let tree = RBTree.from(values); + let tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, 0, 5]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [4, 5]); values = [4, 5, 0, -1, 1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, 0, -1, 1, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -1, 1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [4, 1, -1, 5]); assertEquals([...tree.lvlValues()], [4, 1, 5, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [4, 0, 1, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [4, 0, -1, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -1]); values = [4, 5, 0, -2, 2, -3, -1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, 0, -2, -3, -1, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, -3, -1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [4, -2, -3, 2, -1, 5]); assertEquals([...tree.lvlValues()], [4, -2, 5, -3, 2, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [4, 0, -1, -3, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -1, 2, -3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [4, -2, -3, 0, -1, 5]); assertEquals([...tree.lvlValues()], [4, -2, 5, -3, 0, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals([...tree.nlrValues()], [4, 0, -2, -1, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [4, 0, -2, -3, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, -3]); values = [4, 5, 0, -2, 2, 1, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 1, 3, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, 1, 3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [4, 1, -2, 2, 3, 5]); assertEquals([...tree.lvlValues()], [4, 1, 5, -2, 2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [4, 2, 0, 1, 3, 5]); assertEquals([...tree.lvlValues()], [4, 2, 5, 0, 3, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 3, 1, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 3, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 3, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 1, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, 1]); values = [4, 5, 0, -2, 2, -3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, 0, -2, -3, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, -3]); assertEquals(tree.remove(-3), true); @@ -918,32 +924,32 @@ Deno.test("[collections/RBTree] remove rebalance left", () => { assertEquals([...tree.nlrValues()], [4, 2, -2, 5]); assertEquals([...tree.lvlValues()], [4, 2, 5, -2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [4, 0, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals(tree.remove(4), true); assertEquals([...tree.nlrValues()], [0, -2, 5, 2]); assertEquals([...tree.lvlValues()], [0, -2, 5, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals(tree.remove(5), true); assertEquals([...tree.nlrValues()], [0, -2, 4, 2]); assertEquals([...tree.lvlValues()], [0, -2, 4, 2]); values = [4, 5, 0, -2, 2, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 3, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, 3]); assertEquals(tree.remove(3), true); @@ -953,294 +959,294 @@ Deno.test("[collections/RBTree] remove rebalance left", () => { assertEquals([...tree.nlrValues()], [4, 2, -2, 5]); assertEquals([...tree.lvlValues()], [4, 2, 5, -2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [4, 0, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals(tree.remove(4), true); assertEquals([...tree.nlrValues()], [0, -2, 5, 2]); assertEquals([...tree.lvlValues()], [0, -2, 5, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals(tree.remove(5), true); assertEquals([...tree.nlrValues()], [0, -2, 4, 2]); assertEquals([...tree.lvlValues()], [0, -2, 4, 2]); values = [4, 5, 0, -2, 2, -3, -1, 1, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, 0, -2, -3, -1, 2, 1, 3, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, -3, -1, 1, 3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [4, 1, -2, -3, -1, 2, 3, 5]); assertEquals([...tree.lvlValues()], [4, 1, 5, -2, 2, -3, -1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [4, 0, -1, -3, 2, 1, 3, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -1, 2, -3, 1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [4, 0, -2, -3, -1, 3, 1, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 3, -3, -1, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals([...tree.nlrValues()], [4, 0, -2, -1, 2, 1, 3, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, -1, 1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [4, 0, -2, -3, 2, 1, 3, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, -3, 1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [4, 0, -2, -3, -1, 2, 3, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, -3, -1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals([...tree.nlrValues()], [4, 0, -2, -3, -1, 2, 1, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, -3, -1, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(4), true); assertEquals([...tree.nlrValues()], [0, -2, -3, -1, 2, 1, 5, 3]); assertEquals([...tree.lvlValues()], [0, -2, 2, -3, -1, 1, 5, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(5), true); assertEquals([...tree.nlrValues()], [0, -2, -3, -1, 2, 1, 4, 3]); assertEquals([...tree.lvlValues()], [0, -2, 2, -3, -1, 1, 4, 3]); values = [4, 5, 0, -2, 2, -3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, 0, -2, -3, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, -3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [4, -2, -3, 2, 5]); assertEquals([...tree.lvlValues()], [4, -2, 5, -3, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [4, 0, -3, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -3, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [4, -2, -3, 0, 5]); assertEquals([...tree.lvlValues()], [4, -2, 5, -3, 0]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(4), true); assertEquals([...tree.nlrValues()], [0, -2, -3, 5, 2]); assertEquals([...tree.lvlValues()], [0, -2, 5, -3, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(5), true); assertEquals([...tree.nlrValues()], [0, -2, -3, 4, 2]); assertEquals([...tree.lvlValues()], [0, -2, 4, -3, 2]); values = [4, 5, 0, -2, 2, -1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, 0, -2, -1, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, -1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [4, -1, -2, 2, 5]); assertEquals([...tree.lvlValues()], [4, -1, 5, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [4, 0, -1, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -1, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [4, -1, -2, 0, 5]); assertEquals([...tree.lvlValues()], [4, -1, 5, -2, 0]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(4), true); assertEquals([...tree.nlrValues()], [0, -2, -1, 5, 2]); assertEquals([...tree.lvlValues()], [0, -2, 5, -1, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(5), true); assertEquals([...tree.nlrValues()], [0, -2, -1, 4, 2]); assertEquals([...tree.lvlValues()], [0, -2, 4, -1, 2]); values = [4, 5, 0, -2, 2, 1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 1, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, 1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [4, 1, -2, 2, 5]); assertEquals([...tree.lvlValues()], [4, 1, 5, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [4, 1, 0, 2, 5]); assertEquals([...tree.lvlValues()], [4, 1, 5, 0, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 1, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(4), true); assertEquals([...tree.nlrValues()], [0, -2, 2, 1, 5]); assertEquals([...tree.lvlValues()], [0, -2, 2, 1, 5]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(5), true); assertEquals([...tree.nlrValues()], [0, -2, 2, 1, 4]); assertEquals([...tree.lvlValues()], [0, -2, 2, 1, 4]); values = [4, 5, 0, -2, 2, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 3, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2, 3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [4, 2, -2, 3, 5]); assertEquals([...tree.lvlValues()], [4, 2, 5, -2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [4, 2, 0, 3, 5]); assertEquals([...tree.lvlValues()], [4, 2, 5, 0, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 3, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals([...tree.nlrValues()], [4, 0, -2, 2, 5]); assertEquals([...tree.lvlValues()], [4, 0, 5, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(4), true); assertEquals([...tree.nlrValues()], [0, -2, 3, 2, 5]); assertEquals([...tree.lvlValues()], [0, -2, 3, 2, 5]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(5), true); assertEquals([...tree.nlrValues()], [0, -2, 3, 2, 4]); assertEquals([...tree.lvlValues()], [0, -2, 3, 2, 4]); }); -Deno.test("[collections/RBTree] remove rebalance right", () => { +Deno.test("[collections/RedBlackTree] remove rebalance right", () => { let values = [-4, -5, 0]; - let tree = RBTree.from(values); + let tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -5, 0]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-4, -5]); values = [-4, -5, 0, -1, 1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -5, 0, -1, 1]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -1, 1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-4, -5, 1, -1]); assertEquals([...tree.lvlValues()], [-4, -5, 1, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, 1]); assertEquals([...tree.lvlValues()], [-4, -5, 0, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -1]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -1]); values = [-4, -5, 0, -2, 2, -3, -1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -3, -1, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, -3, -1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-4, -5, -2, -3, 2, -1]); assertEquals([...tree.lvlValues()], [-4, -5, -2, -3, 2, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -1, -3, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -1, 2, -3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-4, -5, -2, -3, 0, -1]); assertEquals([...tree.lvlValues()], [-4, -5, -2, -3, 0, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -1, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, -1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -3, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, -3]); values = [-4, -5, 0, -2, 2, 1, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 2, 1, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, 1, 3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-4, -5, 1, -2, 2, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 1, -2, 2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [-4, -5, 2, 0, 1, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 2, 0, 3, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 3, 1]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 3, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 2, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 2, 1]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, 1]); values = [-4, -5, 0, -2, 2, -3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -3, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, -3]); assertEquals(tree.remove(-3), true); @@ -1250,32 +1256,32 @@ Deno.test("[collections/RBTree] remove rebalance right", () => { assertEquals([...tree.nlrValues()], [-4, -5, 2, -2]); assertEquals([...tree.lvlValues()], [-4, -5, 2, -2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals(tree.remove(-4), true); assertEquals([...tree.nlrValues()], [-2, -5, 0, 2]); assertEquals([...tree.lvlValues()], [-2, -5, 0, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals(tree.remove(-5), true); assertEquals([...tree.nlrValues()], [0, -4, -2, 2]); assertEquals([...tree.lvlValues()], [0, -4, 2, -2]); values = [-4, -5, 0, -2, 2, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 2, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, 3]); assertEquals(tree.remove(3), true); @@ -1285,214 +1291,214 @@ Deno.test("[collections/RBTree] remove rebalance right", () => { assertEquals([...tree.nlrValues()], [-4, -5, 2, -2]); assertEquals([...tree.lvlValues()], [-4, -5, 2, -2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals(tree.remove(-4), true); assertEquals([...tree.nlrValues()], [-2, -5, 0, 2]); assertEquals([...tree.lvlValues()], [-2, -5, 0, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals(tree.remove(-5), true); assertEquals([...tree.nlrValues()], [0, -4, -2, 2]); assertEquals([...tree.lvlValues()], [0, -4, 2, -2]); values = [-4, -5, 0, -2, 2, -3, -1, 1, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -3, -1, 2, 1, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, -3, -1, 1, 3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-4, -5, 1, -2, -3, -1, 2, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 1, -2, 2, -3, -1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -1, -3, 2, 1, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -1, 2, -3, 1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -3, -1, 3, 1]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 3, -3, -1, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -1, 2, 1, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, -1, 1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -3, 2, 1, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, -3, 1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -3, -1, 2, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, -3, -1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -3, -1, 2, 1]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, -3, -1, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-4), true); assertEquals([...tree.nlrValues()], [-3, -5, 0, -2, -1, 2, 1, 3]); assertEquals([...tree.lvlValues()], [-3, -5, 0, -2, 2, -1, 1, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-5), true); assertEquals([...tree.nlrValues()], [0, -2, -4, -3, -1, 2, 1, 3]); assertEquals([...tree.lvlValues()], [0, -2, 2, -4, -1, 1, 3, -3]); values = [-4, -5, 0, -2, 2, -3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -3, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, -3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-4, -5, -2, -3, 2]); assertEquals([...tree.lvlValues()], [-4, -5, -2, -3, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -3, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -3, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-4, -5, -2, -3, 0]); assertEquals([...tree.lvlValues()], [-4, -5, -2, -3, 0]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-3), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-4), true); assertEquals([...tree.nlrValues()], [-3, -5, 0, -2, 2]); assertEquals([...tree.lvlValues()], [-3, -5, 0, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-5), true); assertEquals([...tree.nlrValues()], [0, -3, -4, -2, 2]); assertEquals([...tree.lvlValues()], [0, -3, 2, -4, -2]); values = [-4, -5, 0, -2, 2, -1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, -1, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, -1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-4, -5, -1, -2, 2]); assertEquals([...tree.lvlValues()], [-4, -5, -1, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -1, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -1, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-4, -5, -1, -2, 0]); assertEquals([...tree.lvlValues()], [-4, -5, -1, -2, 0]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-1), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-4), true); assertEquals([...tree.nlrValues()], [-2, -5, 0, -1, 2]); assertEquals([...tree.lvlValues()], [-2, -5, 0, -1, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-5), true); assertEquals([...tree.nlrValues()], [0, -2, -4, -1, 2]); assertEquals([...tree.lvlValues()], [0, -2, 2, -4, -1]); values = [-4, -5, 0, -2, 2, 1]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 2, 1]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, 1]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-4, -5, 1, -2, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 1, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [-4, -5, 1, 0, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 1, 0, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 1]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 1]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(1), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-4), true); assertEquals([...tree.nlrValues()], [-2, -5, 1, 0, 2]); assertEquals([...tree.lvlValues()], [-2, -5, 1, 0, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-5), true); assertEquals([...tree.nlrValues()], [0, -4, -2, 2, 1]); assertEquals([...tree.lvlValues()], [0, -4, 2, -2, 1]); values = [-4, -5, 0, -2, 2, 3]; - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 2, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2, 3]); assertEquals(tree.remove(0), true); assertEquals([...tree.nlrValues()], [-4, -5, 2, -2, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 2, -2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-2), true); assertEquals([...tree.nlrValues()], [-4, -5, 2, 0, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 2, 0, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(2), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 3]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(3), true); assertEquals([...tree.nlrValues()], [-4, -5, 0, -2, 2]); assertEquals([...tree.lvlValues()], [-4, -5, 0, -2, 2]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-4), true); assertEquals([...tree.nlrValues()], [-2, -5, 2, 0, 3]); assertEquals([...tree.lvlValues()], [-2, -5, 2, 0, 3]); - tree = RBTree.from(values); + tree = RedBlackTree.from(values); assertEquals(tree.remove(-5), true); assertEquals([...tree.nlrValues()], [0, -4, -2, 2, 3]); assertEquals([...tree.lvlValues()], [0, -4, 2, -2, 3]); }); -Deno.test("[collections/RBTree] README example", () => { +Deno.test("[collections/RedBlackTree] README example", () => { const values = [3, 10, 13, 4, 6, 7, 1, 14]; - const tree = new RBTree(); + const tree = new RedBlackTree(); values.forEach((value) => tree.insert(value)); assertEquals([...tree], [1, 3, 4, 6, 7, 10, 13, 14]); assertEquals(tree.min(), 1); @@ -1503,7 +1509,7 @@ Deno.test("[collections/RBTree] README example", () => { assertEquals(tree.remove(7), true); assertEquals([...tree], [1, 3, 4, 6, 10, 13, 14]); - const invertedTree = new RBTree(descend); + const invertedTree = new RedBlackTree(descend); values.forEach((value) => invertedTree.insert(value)); assertEquals([...invertedTree], [14, 13, 10, 7, 6, 4, 3, 1]); assertEquals(invertedTree.min(), 14); @@ -1514,7 +1520,7 @@ Deno.test("[collections/RBTree] README example", () => { assertEquals(invertedTree.remove(7), true); assertEquals([...invertedTree], [14, 13, 10, 6, 4, 3, 1]); - const words = new RBTree((a, b) => + const words = new RedBlackTree((a, b) => ascend(a.length, b.length) || ascend(a, b) ); ["truck", "car", "helicopter", "tank", "train", "suv", "semi", "van"] diff --git a/collections/sort_by.ts b/collections/sort_by.ts index 79a0cb87b760..5f048c9939ad 100644 --- a/collections/sort_by.ts +++ b/collections/sort_by.ts @@ -26,19 +26,19 @@ */ export function sortBy( array: readonly T[], - selector: ((el: T) => number), + selector: (el: T) => number, ): T[]; export function sortBy( array: readonly T[], - selector: ((el: T) => string), + selector: (el: T) => string, ): T[]; export function sortBy( array: readonly T[], - selector: ((el: T) => bigint), + selector: (el: T) => bigint, ): T[]; export function sortBy( array: readonly T[], - selector: ((el: T) => Date), + selector: (el: T) => Date, ): T[]; export function sortBy( array: readonly T[],