bst-typed 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +3413 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/esm/index.mjs +3406 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/types/data-structures/base/index.d.ts +2 -1
- package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +182 -2
- package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +135 -2
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +291 -2
- package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +174 -1
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +754 -29
- package/dist/types/data-structures/binary-tree/bst.d.ts +413 -12
- package/dist/types/data-structures/binary-tree/index.d.ts +3 -2
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +208 -3
- package/dist/types/data-structures/binary-tree/segment-tree.d.ts +160 -1
- package/dist/types/data-structures/binary-tree/tree-counter.d.ts +190 -2
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +270 -2
- package/dist/types/data-structures/graph/abstract-graph.d.ts +340 -14
- package/dist/types/data-structures/graph/directed-graph.d.ts +207 -1
- package/dist/types/data-structures/graph/index.d.ts +2 -1
- package/dist/types/data-structures/graph/map-graph.d.ts +78 -1
- package/dist/types/data-structures/graph/undirected-graph.d.ts +188 -1
- package/dist/types/data-structures/hash/hash-map.d.ts +345 -19
- package/dist/types/data-structures/hash/index.d.ts +0 -1
- package/dist/types/data-structures/heap/heap.d.ts +503 -5
- package/dist/types/data-structures/heap/index.d.ts +2 -0
- package/dist/types/data-structures/heap/max-heap.d.ts +32 -1
- package/dist/types/data-structures/heap/min-heap.d.ts +33 -1
- package/dist/types/data-structures/index.d.ts +7 -7
- package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +769 -2
- package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +451 -2
- package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +27 -4
- package/dist/types/data-structures/matrix/index.d.ts +1 -1
- package/dist/types/data-structures/matrix/matrix.d.ts +168 -7
- package/dist/types/data-structures/matrix/navigator.d.ts +54 -13
- package/dist/types/data-structures/priority-queue/max-priority-queue.d.ts +27 -1
- package/dist/types/data-structures/priority-queue/min-priority-queue.d.ts +26 -1
- package/dist/types/data-structures/priority-queue/priority-queue.d.ts +15 -2
- package/dist/types/data-structures/queue/deque.d.ts +431 -4
- package/dist/types/data-structures/queue/queue.d.ts +308 -4
- package/dist/types/data-structures/stack/stack.d.ts +306 -2
- package/dist/types/data-structures/tree/tree.d.ts +62 -1
- package/dist/types/data-structures/trie/trie.d.ts +350 -4
- package/dist/types/index.d.ts +13 -2
- package/dist/{interfaces → types/interfaces}/binary-tree.d.ts +3 -3
- package/dist/types/types/data-structures/base/index.d.ts +1 -0
- package/dist/types/types/data-structures/binary-tree/avl-tree-counter.d.ts +2 -0
- package/dist/types/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +2 -0
- package/dist/types/types/data-structures/binary-tree/avl-tree.d.ts +2 -0
- package/dist/types/types/data-structures/binary-tree/binary-indexed-tree.d.ts +1 -0
- package/dist/types/types/data-structures/binary-tree/binary-tree.d.ts +29 -0
- package/dist/types/types/data-structures/binary-tree/bst.d.ts +12 -0
- package/dist/{data-structures → types/types/data-structures}/binary-tree/index.d.ts +2 -3
- package/dist/types/types/data-structures/binary-tree/red-black-tree.d.ts +3 -0
- package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -0
- package/dist/types/types/data-structures/binary-tree/tree-counter.d.ts +2 -0
- package/dist/types/types/data-structures/binary-tree/tree-multi-map.d.ts +2 -0
- package/dist/types/types/data-structures/graph/abstract-graph.d.ts +14 -0
- package/dist/types/types/data-structures/graph/directed-graph.d.ts +1 -0
- package/dist/{data-structures → types/types/data-structures}/graph/index.d.ts +1 -2
- package/dist/types/types/data-structures/graph/map-graph.d.ts +1 -0
- package/dist/types/types/data-structures/graph/undirected-graph.d.ts +1 -0
- package/dist/types/types/data-structures/hash/hash-map.d.ts +19 -0
- package/dist/types/types/data-structures/hash/index.d.ts +2 -0
- package/dist/types/types/data-structures/heap/heap.d.ts +5 -0
- package/dist/types/types/data-structures/heap/index.d.ts +1 -0
- package/dist/types/types/data-structures/heap/max-heap.d.ts +1 -0
- package/dist/types/types/data-structures/heap/min-heap.d.ts +1 -0
- package/dist/types/types/data-structures/linked-list/doubly-linked-list.d.ts +2 -0
- package/dist/types/types/data-structures/linked-list/singly-linked-list.d.ts +2 -0
- package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +4 -0
- package/dist/types/types/data-structures/matrix/matrix.d.ts +7 -0
- package/dist/types/types/data-structures/matrix/navigator.d.ts +14 -0
- package/dist/types/types/data-structures/priority-queue/max-priority-queue.d.ts +1 -0
- package/dist/types/types/data-structures/priority-queue/min-priority-queue.d.ts +1 -0
- package/dist/types/types/data-structures/priority-queue/priority-queue.d.ts +2 -0
- package/dist/types/types/data-structures/queue/deque.d.ts +4 -0
- package/dist/types/types/data-structures/queue/queue.d.ts +4 -0
- package/dist/types/types/data-structures/stack/stack.d.ts +2 -0
- package/dist/types/types/data-structures/tree/tree.d.ts +1 -0
- package/dist/types/types/data-structures/trie/trie.d.ts +4 -0
- package/dist/types/types/index.d.ts +3 -0
- package/dist/types/types/utils/index.d.ts +2 -0
- package/dist/types/types/utils/utils.d.ts +22 -0
- package/dist/types/utils/index.d.ts +1 -1
- package/dist/types/utils/utils.d.ts +209 -22
- package/dist/umd/bst-typed.js +3410 -0
- package/dist/umd/bst-typed.js.map +1 -0
- package/dist/umd/bst-typed.min.js +13 -0
- package/dist/umd/bst-typed.min.js.map +1 -0
- package/package.json +24 -5
- package/src/data-structures/binary-tree/avl-tree-counter.ts +8 -11
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +6 -11
- package/src/data-structures/binary-tree/avl-tree.ts +6 -8
- package/src/data-structures/binary-tree/binary-tree.ts +13 -15
- package/src/data-structures/binary-tree/bst.ts +6 -11
- package/src/data-structures/binary-tree/red-black-tree.ts +6 -11
- package/src/data-structures/binary-tree/tree-counter.ts +8 -13
- package/src/data-structures/binary-tree/tree-multi-map.ts +6 -11
- package/src/data-structures/heap/heap.ts +5 -5
- package/src/data-structures/linked-list/singly-linked-list.ts +2 -2
- package/src/interfaces/binary-tree.ts +3 -3
- package/tsconfig.base.json +23 -0
- package/tsconfig.json +8 -34
- package/tsconfig.test.json +8 -0
- package/tsconfig.types.json +15 -0
- package/tsup.config.js +28 -0
- package/tsup.node.config.js +37 -0
- package/dist/common/index.js +0 -28
- package/dist/constants/index.js +0 -8
- package/dist/data-structures/base/index.d.ts +0 -2
- package/dist/data-structures/base/index.js +0 -18
- package/dist/data-structures/base/iterable-element-base.js +0 -243
- package/dist/data-structures/base/iterable-entry-base.js +0 -183
- package/dist/data-structures/base/linear-base.js +0 -415
- package/dist/data-structures/binary-tree/avl-tree-counter.d.ts +0 -182
- package/dist/data-structures/binary-tree/avl-tree-counter.js +0 -374
- package/dist/data-structures/binary-tree/avl-tree-multi-map.d.ts +0 -135
- package/dist/data-structures/binary-tree/avl-tree-multi-map.js +0 -250
- package/dist/data-structures/binary-tree/avl-tree.d.ts +0 -291
- package/dist/data-structures/binary-tree/avl-tree.js +0 -611
- package/dist/data-structures/binary-tree/binary-indexed-tree.d.ts +0 -174
- package/dist/data-structures/binary-tree/binary-indexed-tree.js +0 -294
- package/dist/data-structures/binary-tree/binary-tree.d.ts +0 -754
- package/dist/data-structures/binary-tree/binary-tree.js +0 -1925
- package/dist/data-structures/binary-tree/bst.d.ts +0 -413
- package/dist/data-structures/binary-tree/bst.js +0 -903
- package/dist/data-structures/binary-tree/index.js +0 -26
- package/dist/data-structures/binary-tree/red-black-tree.d.ts +0 -208
- package/dist/data-structures/binary-tree/red-black-tree.js +0 -546
- package/dist/data-structures/binary-tree/segment-tree.d.ts +0 -160
- package/dist/data-structures/binary-tree/segment-tree.js +0 -297
- package/dist/data-structures/binary-tree/tree-counter.d.ts +0 -190
- package/dist/data-structures/binary-tree/tree-counter.js +0 -413
- package/dist/data-structures/binary-tree/tree-multi-map.d.ts +0 -270
- package/dist/data-structures/binary-tree/tree-multi-map.js +0 -384
- package/dist/data-structures/graph/abstract-graph.d.ts +0 -340
- package/dist/data-structures/graph/abstract-graph.js +0 -896
- package/dist/data-structures/graph/directed-graph.d.ts +0 -207
- package/dist/data-structures/graph/directed-graph.js +0 -525
- package/dist/data-structures/graph/index.js +0 -20
- package/dist/data-structures/graph/map-graph.d.ts +0 -78
- package/dist/data-structures/graph/map-graph.js +0 -107
- package/dist/data-structures/graph/undirected-graph.d.ts +0 -188
- package/dist/data-structures/graph/undirected-graph.js +0 -424
- package/dist/data-structures/hash/hash-map.d.ts +0 -345
- package/dist/data-structures/hash/hash-map.js +0 -692
- package/dist/data-structures/hash/index.d.ts +0 -1
- package/dist/data-structures/hash/index.js +0 -17
- package/dist/data-structures/heap/heap.d.ts +0 -503
- package/dist/data-structures/heap/heap.js +0 -901
- package/dist/data-structures/heap/index.d.ts +0 -3
- package/dist/data-structures/heap/index.js +0 -19
- package/dist/data-structures/heap/max-heap.d.ts +0 -32
- package/dist/data-structures/heap/max-heap.js +0 -40
- package/dist/data-structures/heap/min-heap.d.ts +0 -33
- package/dist/data-structures/heap/min-heap.js +0 -31
- package/dist/data-structures/index.js +0 -28
- package/dist/data-structures/linked-list/doubly-linked-list.d.ts +0 -769
- package/dist/data-structures/linked-list/doubly-linked-list.js +0 -1111
- package/dist/data-structures/linked-list/index.js +0 -19
- package/dist/data-structures/linked-list/singly-linked-list.d.ts +0 -451
- package/dist/data-structures/linked-list/singly-linked-list.js +0 -850
- package/dist/data-structures/linked-list/skip-linked-list.d.ts +0 -27
- package/dist/data-structures/linked-list/skip-linked-list.js +0 -144
- package/dist/data-structures/matrix/index.js +0 -18
- package/dist/data-structures/matrix/matrix.d.ts +0 -168
- package/dist/data-structures/matrix/matrix.js +0 -448
- package/dist/data-structures/matrix/navigator.d.ts +0 -55
- package/dist/data-structures/matrix/navigator.js +0 -111
- package/dist/data-structures/priority-queue/index.js +0 -19
- package/dist/data-structures/priority-queue/max-priority-queue.d.ts +0 -27
- package/dist/data-structures/priority-queue/max-priority-queue.js +0 -34
- package/dist/data-structures/priority-queue/min-priority-queue.d.ts +0 -26
- package/dist/data-structures/priority-queue/min-priority-queue.js +0 -24
- package/dist/data-structures/priority-queue/priority-queue.d.ts +0 -15
- package/dist/data-structures/priority-queue/priority-queue.js +0 -20
- package/dist/data-structures/queue/deque.d.ts +0 -431
- package/dist/data-structures/queue/deque.js +0 -879
- package/dist/data-structures/queue/index.js +0 -18
- package/dist/data-structures/queue/queue.d.ts +0 -308
- package/dist/data-structures/queue/queue.js +0 -473
- package/dist/data-structures/stack/index.js +0 -17
- package/dist/data-structures/stack/stack.d.ts +0 -306
- package/dist/data-structures/stack/stack.js +0 -401
- package/dist/data-structures/tree/index.js +0 -17
- package/dist/data-structures/tree/tree.d.ts +0 -62
- package/dist/data-structures/tree/tree.js +0 -107
- package/dist/data-structures/trie/index.js +0 -17
- package/dist/data-structures/trie/trie.d.ts +0 -350
- package/dist/data-structures/trie/trie.js +0 -610
- package/dist/index.d.ts +0 -14
- package/dist/index.js +0 -30
- package/dist/interfaces/binary-tree.js +0 -2
- package/dist/interfaces/doubly-linked-list.js +0 -2
- package/dist/interfaces/graph.js +0 -2
- package/dist/interfaces/heap.js +0 -2
- package/dist/interfaces/index.js +0 -24
- package/dist/interfaces/navigator.js +0 -2
- package/dist/interfaces/priority-queue.js +0 -2
- package/dist/interfaces/segment-tree.js +0 -2
- package/dist/interfaces/singly-linked-list.js +0 -2
- package/dist/types/common.js +0 -2
- package/dist/types/data-structures/base/base.js +0 -2
- package/dist/types/data-structures/base/index.js +0 -17
- package/dist/types/data-structures/binary-tree/avl-tree-counter.js +0 -2
- package/dist/types/data-structures/binary-tree/avl-tree-multi-map.js +0 -2
- package/dist/types/data-structures/binary-tree/avl-tree.js +0 -2
- package/dist/types/data-structures/binary-tree/binary-indexed-tree.js +0 -2
- package/dist/types/data-structures/binary-tree/binary-tree.js +0 -2
- package/dist/types/data-structures/binary-tree/bst.js +0 -2
- package/dist/types/data-structures/binary-tree/index.js +0 -25
- package/dist/types/data-structures/binary-tree/red-black-tree.js +0 -2
- package/dist/types/data-structures/binary-tree/segment-tree.js +0 -2
- package/dist/types/data-structures/binary-tree/tree-counter.js +0 -2
- package/dist/types/data-structures/binary-tree/tree-multi-map.js +0 -2
- package/dist/types/data-structures/graph/abstract-graph.js +0 -2
- package/dist/types/data-structures/graph/directed-graph.js +0 -2
- package/dist/types/data-structures/graph/index.js +0 -19
- package/dist/types/data-structures/graph/map-graph.js +0 -2
- package/dist/types/data-structures/graph/undirected-graph.js +0 -2
- package/dist/types/data-structures/hash/hash-map.js +0 -2
- package/dist/types/data-structures/hash/index.js +0 -17
- package/dist/types/data-structures/heap/heap.js +0 -2
- package/dist/types/data-structures/heap/index.js +0 -17
- package/dist/types/data-structures/heap/max-heap.js +0 -2
- package/dist/types/data-structures/heap/min-heap.js +0 -2
- package/dist/types/data-structures/index.js +0 -28
- package/dist/types/data-structures/linked-list/doubly-linked-list.js +0 -2
- package/dist/types/data-structures/linked-list/index.js +0 -19
- package/dist/types/data-structures/linked-list/singly-linked-list.js +0 -2
- package/dist/types/data-structures/linked-list/skip-linked-list.js +0 -2
- package/dist/types/data-structures/matrix/index.js +0 -18
- package/dist/types/data-structures/matrix/matrix.js +0 -2
- package/dist/types/data-structures/matrix/navigator.js +0 -2
- package/dist/types/data-structures/priority-queue/index.js +0 -19
- package/dist/types/data-structures/priority-queue/max-priority-queue.js +0 -2
- package/dist/types/data-structures/priority-queue/min-priority-queue.js +0 -2
- package/dist/types/data-structures/priority-queue/priority-queue.js +0 -2
- package/dist/types/data-structures/queue/deque.js +0 -2
- package/dist/types/data-structures/queue/index.js +0 -18
- package/dist/types/data-structures/queue/queue.js +0 -2
- package/dist/types/data-structures/stack/index.js +0 -17
- package/dist/types/data-structures/stack/stack.js +0 -2
- package/dist/types/data-structures/tree/index.js +0 -17
- package/dist/types/data-structures/tree/tree.js +0 -2
- package/dist/types/data-structures/trie/index.js +0 -17
- package/dist/types/data-structures/trie/trie.js +0 -2
- package/dist/types/index.js +0 -19
- package/dist/types/utils/index.js +0 -18
- package/dist/types/utils/utils.js +0 -2
- package/dist/types/utils/validate-type.js +0 -2
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.js +0 -18
- package/dist/utils/number.js +0 -24
- package/dist/utils/utils.d.ts +0 -209
- package/dist/utils/utils.js +0 -353
- package/dist/{common → types/common}/index.d.ts +0 -0
- package/dist/{constants → types/constants}/index.d.ts +0 -0
- package/dist/{data-structures → types/data-structures}/base/iterable-element-base.d.ts +0 -0
- package/dist/{data-structures → types/data-structures}/base/iterable-entry-base.d.ts +0 -0
- package/dist/{data-structures → types/data-structures}/base/linear-base.d.ts +0 -0
- package/dist/{interfaces → types/interfaces}/doubly-linked-list.d.ts +0 -0
- package/dist/{interfaces → types/interfaces}/graph.d.ts +0 -0
- package/dist/{interfaces → types/interfaces}/heap.d.ts +0 -0
- package/dist/{interfaces → types/interfaces}/index.d.ts +0 -0
- package/dist/{interfaces → types/interfaces}/navigator.d.ts +0 -0
- package/dist/{interfaces → types/interfaces}/priority-queue.d.ts +0 -0
- package/dist/{interfaces → types/interfaces}/segment-tree.d.ts +0 -0
- package/dist/{interfaces → types/interfaces}/singly-linked-list.d.ts +0 -0
- package/dist/types/{common.d.ts → types/common.d.ts} +0 -0
- package/dist/types/{data-structures → types/data-structures}/base/base.d.ts +0 -0
- package/dist/{data-structures → types/types/data-structures}/index.d.ts +7 -7
- package/dist/{data-structures → types/types/data-structures}/linked-list/index.d.ts +0 -0
- package/dist/{data-structures → types/types/data-structures}/matrix/index.d.ts +1 -1
- /package/dist/{data-structures → types/types/data-structures}/priority-queue/index.d.ts +0 -0
- /package/dist/{data-structures → types/types/data-structures}/queue/index.d.ts +0 -0
- /package/dist/{data-structures → types/types/data-structures}/stack/index.d.ts +0 -0
- /package/dist/{data-structures → types/types/data-structures}/tree/index.d.ts +0 -0
- /package/dist/{data-structures → types/types/data-structures}/trie/index.d.ts +0 -0
- /package/dist/types/{utils → types/utils}/validate-type.d.ts +0 -0
- /package/dist/{utils → types/utils}/number.d.ts +0 -0
|
@@ -0,0 +1,3410 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var bstTyped = (() => {
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
22
|
+
|
|
23
|
+
// src/index.ts
|
|
24
|
+
var src_exports = {};
|
|
25
|
+
__export(src_exports, {
|
|
26
|
+
BST: () => BST,
|
|
27
|
+
BSTNode: () => BSTNode,
|
|
28
|
+
BinaryTree: () => BinaryTree,
|
|
29
|
+
BinaryTreeNode: () => BinaryTreeNode,
|
|
30
|
+
DFSOperation: () => DFSOperation,
|
|
31
|
+
Range: () => Range
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// src/utils/utils.ts
|
|
35
|
+
function isPrimitiveComparable(value) {
|
|
36
|
+
const valueType = typeof value;
|
|
37
|
+
if (valueType === "number") return true;
|
|
38
|
+
return valueType === "bigint" || valueType === "string" || valueType === "boolean";
|
|
39
|
+
}
|
|
40
|
+
function tryObjectToPrimitive(obj) {
|
|
41
|
+
if (typeof obj.valueOf === "function") {
|
|
42
|
+
const valueOfResult = obj.valueOf();
|
|
43
|
+
if (valueOfResult !== obj) {
|
|
44
|
+
if (isPrimitiveComparable(valueOfResult)) return valueOfResult;
|
|
45
|
+
if (typeof valueOfResult === "object" && valueOfResult !== null) return tryObjectToPrimitive(valueOfResult);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (typeof obj.toString === "function") {
|
|
49
|
+
const stringResult = obj.toString();
|
|
50
|
+
if (stringResult !== "[object Object]") return stringResult;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function isComparable(value, isForceObjectComparable = false) {
|
|
55
|
+
if (value === null || value === void 0) return false;
|
|
56
|
+
if (isPrimitiveComparable(value)) return true;
|
|
57
|
+
if (typeof value !== "object") return false;
|
|
58
|
+
if (value instanceof Date) return true;
|
|
59
|
+
if (isForceObjectComparable) return true;
|
|
60
|
+
const comparableValue = tryObjectToPrimitive(value);
|
|
61
|
+
if (comparableValue === null || comparableValue === void 0) return false;
|
|
62
|
+
return isPrimitiveComparable(comparableValue);
|
|
63
|
+
}
|
|
64
|
+
var makeTrampolineThunk = (computation) => ({
|
|
65
|
+
isThunk: true,
|
|
66
|
+
// Marker indicating this is a thunk
|
|
67
|
+
fn: computation
|
|
68
|
+
// The deferred computation function
|
|
69
|
+
});
|
|
70
|
+
var isTrampolineThunk = (value) => typeof value === "object" && // Must be an object
|
|
71
|
+
value !== null && // Must not be null
|
|
72
|
+
"isThunk" in value && // Must have the 'isThunk' property
|
|
73
|
+
value.isThunk;
|
|
74
|
+
function trampoline(initial) {
|
|
75
|
+
let current = initial;
|
|
76
|
+
while (isTrampolineThunk(current)) {
|
|
77
|
+
current = current.fn();
|
|
78
|
+
}
|
|
79
|
+
return current;
|
|
80
|
+
}
|
|
81
|
+
function makeTrampoline(fn) {
|
|
82
|
+
return (...args) => trampoline(fn(...args));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/data-structures/base/iterable-element-base.ts
|
|
86
|
+
var IterableElementBase = class {
|
|
87
|
+
/**
|
|
88
|
+
* Create a new iterable base.
|
|
89
|
+
*
|
|
90
|
+
* @param options Optional behavior overrides. When provided, a `toElementFn`
|
|
91
|
+
* is used to convert a raw element (`R`) into a public element (`E`).
|
|
92
|
+
*
|
|
93
|
+
* @remarks
|
|
94
|
+
* Time O(1), Space O(1).
|
|
95
|
+
*/
|
|
96
|
+
constructor(options) {
|
|
97
|
+
/**
|
|
98
|
+
* The converter used to transform a raw element (`R`) into a public element (`E`).
|
|
99
|
+
*
|
|
100
|
+
* @remarks
|
|
101
|
+
* Time O(1), Space O(1).
|
|
102
|
+
*/
|
|
103
|
+
__publicField(this, "_toElementFn");
|
|
104
|
+
if (options) {
|
|
105
|
+
const { toElementFn } = options;
|
|
106
|
+
if (typeof toElementFn === "function") this._toElementFn = toElementFn;
|
|
107
|
+
else if (toElementFn) throw new TypeError("toElementFn must be a function type");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Exposes the current `toElementFn`, if configured.
|
|
112
|
+
*
|
|
113
|
+
* @returns The converter function or `undefined` when not set.
|
|
114
|
+
* @remarks
|
|
115
|
+
* Time O(1), Space O(1).
|
|
116
|
+
*/
|
|
117
|
+
get toElementFn() {
|
|
118
|
+
return this._toElementFn;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Returns an iterator over the structure's elements.
|
|
122
|
+
*
|
|
123
|
+
* @param args Optional iterator arguments forwarded to the internal iterator.
|
|
124
|
+
* @returns An `IterableIterator<E>` that yields the elements in traversal order.
|
|
125
|
+
*
|
|
126
|
+
* @remarks
|
|
127
|
+
* Producing the iterator is O(1); consuming the entire iterator is Time O(n) with O(1) extra space.
|
|
128
|
+
*/
|
|
129
|
+
*[Symbol.iterator](...args) {
|
|
130
|
+
yield* this._getIterator(...args);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Returns an iterator over the values (alias of the default iterator).
|
|
134
|
+
*
|
|
135
|
+
* @returns An `IterableIterator<E>` over all elements.
|
|
136
|
+
* @remarks
|
|
137
|
+
* Creating the iterator is O(1); full iteration is Time O(n), Space O(1).
|
|
138
|
+
*/
|
|
139
|
+
*values() {
|
|
140
|
+
for (const item of this) yield item;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Tests whether all elements satisfy the predicate.
|
|
144
|
+
*
|
|
145
|
+
* @template TReturn
|
|
146
|
+
* @param predicate Function invoked for each element with signature `(value, index, self)`.
|
|
147
|
+
* @param thisArg Optional `this` binding for the predicate.
|
|
148
|
+
* @returns `true` if every element passes; otherwise `false`.
|
|
149
|
+
*
|
|
150
|
+
* @remarks
|
|
151
|
+
* Time O(n) in the worst case; may exit early when the first failure is found. Space O(1).
|
|
152
|
+
*/
|
|
153
|
+
every(predicate, thisArg) {
|
|
154
|
+
let index = 0;
|
|
155
|
+
for (const item of this) {
|
|
156
|
+
if (thisArg === void 0) {
|
|
157
|
+
if (!predicate(item, index++, this)) return false;
|
|
158
|
+
} else {
|
|
159
|
+
const fn = predicate;
|
|
160
|
+
if (!fn.call(thisArg, item, index++, this)) return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Tests whether at least one element satisfies the predicate.
|
|
167
|
+
*
|
|
168
|
+
* @param predicate Function invoked for each element with signature `(value, index, self)`.
|
|
169
|
+
* @param thisArg Optional `this` binding for the predicate.
|
|
170
|
+
* @returns `true` if any element passes; otherwise `false`.
|
|
171
|
+
*
|
|
172
|
+
* @remarks
|
|
173
|
+
* Time O(n) in the worst case; may exit early on first success. Space O(1).
|
|
174
|
+
*/
|
|
175
|
+
some(predicate, thisArg) {
|
|
176
|
+
let index = 0;
|
|
177
|
+
for (const item of this) {
|
|
178
|
+
if (thisArg === void 0) {
|
|
179
|
+
if (predicate(item, index++, this)) return true;
|
|
180
|
+
} else {
|
|
181
|
+
const fn = predicate;
|
|
182
|
+
if (fn.call(thisArg, item, index++, this)) return true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Invokes a callback for each element in iteration order.
|
|
189
|
+
*
|
|
190
|
+
* @param callbackfn Function invoked per element with signature `(value, index, self)`.
|
|
191
|
+
* @param thisArg Optional `this` binding for the callback.
|
|
192
|
+
* @returns `void`.
|
|
193
|
+
*
|
|
194
|
+
* @remarks
|
|
195
|
+
* Time O(n), Space O(1).
|
|
196
|
+
*/
|
|
197
|
+
forEach(callbackfn, thisArg) {
|
|
198
|
+
let index = 0;
|
|
199
|
+
for (const item of this) {
|
|
200
|
+
if (thisArg === void 0) {
|
|
201
|
+
callbackfn(item, index++, this);
|
|
202
|
+
} else {
|
|
203
|
+
const fn = callbackfn;
|
|
204
|
+
fn.call(thisArg, item, index++, this);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Implementation signature
|
|
209
|
+
find(predicate, thisArg) {
|
|
210
|
+
let index = 0;
|
|
211
|
+
for (const item of this) {
|
|
212
|
+
if (thisArg === void 0) {
|
|
213
|
+
if (predicate(item, index++, this)) return item;
|
|
214
|
+
} else {
|
|
215
|
+
const fn = predicate;
|
|
216
|
+
if (fn.call(thisArg, item, index++, this)) return item;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Checks whether a strictly-equal element exists in the structure.
|
|
223
|
+
*
|
|
224
|
+
* @param element The element to test with `===` equality.
|
|
225
|
+
* @returns `true` if an equal element is found; otherwise `false`.
|
|
226
|
+
*
|
|
227
|
+
* @remarks
|
|
228
|
+
* Time O(n) in the worst case. Space O(1).
|
|
229
|
+
*/
|
|
230
|
+
has(element) {
|
|
231
|
+
for (const ele of this) if (ele === element) return true;
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Reduces all elements to a single accumulated value.
|
|
236
|
+
*
|
|
237
|
+
* @overload
|
|
238
|
+
* @param callbackfn Reducer of signature `(acc, value, index, self) => nextAcc`. The first element is used as the initial accumulator.
|
|
239
|
+
* @returns The final accumulated value typed as `E`.
|
|
240
|
+
*
|
|
241
|
+
* @overload
|
|
242
|
+
* @param callbackfn Reducer of signature `(acc, value, index, self) => nextAcc`.
|
|
243
|
+
* @param initialValue The initial accumulator value of type `E`.
|
|
244
|
+
* @returns The final accumulated value typed as `E`.
|
|
245
|
+
*
|
|
246
|
+
* @overload
|
|
247
|
+
* @template U The accumulator type when it differs from `E`.
|
|
248
|
+
* @param callbackfn Reducer of signature `(acc: U, value, index, self) => U`.
|
|
249
|
+
* @param initialValue The initial accumulator value of type `U`.
|
|
250
|
+
* @returns The final accumulated value typed as `U`.
|
|
251
|
+
*
|
|
252
|
+
* @remarks
|
|
253
|
+
* Time O(n), Space O(1). Throws if called on an empty structure without `initialValue`.
|
|
254
|
+
*/
|
|
255
|
+
reduce(callbackfn, initialValue) {
|
|
256
|
+
let index = 0;
|
|
257
|
+
const iter = this[Symbol.iterator]();
|
|
258
|
+
let acc;
|
|
259
|
+
if (arguments.length >= 2) {
|
|
260
|
+
acc = initialValue;
|
|
261
|
+
} else {
|
|
262
|
+
const first = iter.next();
|
|
263
|
+
if (first.done) throw new TypeError("Reduce of empty structure with no initial value");
|
|
264
|
+
acc = first.value;
|
|
265
|
+
index = 1;
|
|
266
|
+
}
|
|
267
|
+
for (const value of iter) {
|
|
268
|
+
acc = callbackfn(acc, value, index++, this);
|
|
269
|
+
}
|
|
270
|
+
return acc;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Materializes the elements into a new array.
|
|
274
|
+
*
|
|
275
|
+
* @returns A shallow array copy of the iteration order.
|
|
276
|
+
* @remarks
|
|
277
|
+
* Time O(n), Space O(n).
|
|
278
|
+
*/
|
|
279
|
+
toArray() {
|
|
280
|
+
return [...this];
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Returns a representation of the structure suitable for quick visualization.
|
|
284
|
+
* Defaults to an array of elements; subclasses may override to provide richer visuals.
|
|
285
|
+
*
|
|
286
|
+
* @returns A visual representation (array by default).
|
|
287
|
+
* @remarks
|
|
288
|
+
* Time O(n), Space O(n).
|
|
289
|
+
*/
|
|
290
|
+
toVisual() {
|
|
291
|
+
return [...this];
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Prints `toVisual()` to the console. Intended for quick debugging.
|
|
295
|
+
*
|
|
296
|
+
* @returns `void`.
|
|
297
|
+
* @remarks
|
|
298
|
+
* Time O(n) due to materialization, Space O(n) for the intermediate representation.
|
|
299
|
+
*/
|
|
300
|
+
print() {
|
|
301
|
+
console.log(this.toVisual());
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// src/data-structures/base/linear-base.ts
|
|
306
|
+
var LinearBase = class _LinearBase extends IterableElementBase {
|
|
307
|
+
/**
|
|
308
|
+
* Construct a linear container with runtime options.
|
|
309
|
+
* @param options - `{ maxLen?, ... }` bounds/behavior options.
|
|
310
|
+
* @remarks Time O(1), Space O(1)
|
|
311
|
+
*/
|
|
312
|
+
constructor(options) {
|
|
313
|
+
super(options);
|
|
314
|
+
__publicField(this, "_maxLen", -1);
|
|
315
|
+
if (options) {
|
|
316
|
+
const { maxLen } = options;
|
|
317
|
+
if (typeof maxLen === "number" && maxLen > 0 && maxLen % 1 === 0) this._maxLen = maxLen;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Upper bound for length (if positive), or `-1` when unbounded.
|
|
322
|
+
* @returns Maximum allowed length.
|
|
323
|
+
* @remarks Time O(1), Space O(1)
|
|
324
|
+
*/
|
|
325
|
+
get maxLen() {
|
|
326
|
+
return this._maxLen;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* First index of a value from the left.
|
|
330
|
+
* @param searchElement - Value to match.
|
|
331
|
+
* @param fromIndex - Start position (supports negative index).
|
|
332
|
+
* @returns Index or `-1` if not found.
|
|
333
|
+
* @remarks Time O(n), Space O(1)
|
|
334
|
+
*/
|
|
335
|
+
indexOf(searchElement, fromIndex = 0) {
|
|
336
|
+
if (this.length === 0) return -1;
|
|
337
|
+
if (fromIndex < 0) fromIndex = this.length + fromIndex;
|
|
338
|
+
if (fromIndex < 0) fromIndex = 0;
|
|
339
|
+
for (let i = fromIndex; i < this.length; i++) {
|
|
340
|
+
const element = this.at(i);
|
|
341
|
+
if (element === searchElement) return i;
|
|
342
|
+
}
|
|
343
|
+
return -1;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Last index of a value from the right.
|
|
347
|
+
* @param searchElement - Value to match.
|
|
348
|
+
* @param fromIndex - Start position (supports negative index).
|
|
349
|
+
* @returns Index or `-1` if not found.
|
|
350
|
+
* @remarks Time O(n), Space O(1)
|
|
351
|
+
*/
|
|
352
|
+
lastIndexOf(searchElement, fromIndex = this.length - 1) {
|
|
353
|
+
if (this.length === 0) return -1;
|
|
354
|
+
if (fromIndex >= this.length) fromIndex = this.length - 1;
|
|
355
|
+
if (fromIndex < 0) fromIndex = this.length + fromIndex;
|
|
356
|
+
for (let i = fromIndex; i >= 0; i--) {
|
|
357
|
+
const element = this.at(i);
|
|
358
|
+
if (element === searchElement) return i;
|
|
359
|
+
}
|
|
360
|
+
return -1;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Find the first index matching a predicate.
|
|
364
|
+
* @param predicate - `(element, index, self) => boolean`.
|
|
365
|
+
* @param thisArg - Optional `this` for callback.
|
|
366
|
+
* @returns Index or `-1`.
|
|
367
|
+
* @remarks Time O(n), Space O(1)
|
|
368
|
+
*/
|
|
369
|
+
findIndex(predicate, thisArg) {
|
|
370
|
+
for (let i = 0; i < this.length; i++) {
|
|
371
|
+
const item = this.at(i);
|
|
372
|
+
if (item !== void 0 && predicate.call(thisArg, item, i, this)) return i;
|
|
373
|
+
}
|
|
374
|
+
return -1;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Concatenate elements and/or containers.
|
|
378
|
+
* @param items - Elements or other containers.
|
|
379
|
+
* @returns New container with combined elements (`this` type).
|
|
380
|
+
* @remarks Time O(sum(length)), Space O(sum(length))
|
|
381
|
+
*/
|
|
382
|
+
concat(...items) {
|
|
383
|
+
const newList = this.clone();
|
|
384
|
+
for (const item of items) {
|
|
385
|
+
if (item instanceof _LinearBase) {
|
|
386
|
+
newList.pushMany(item);
|
|
387
|
+
} else {
|
|
388
|
+
newList.push(item);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return newList;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* In-place stable order via array sort semantics.
|
|
395
|
+
* @param compareFn - Comparator `(a, b) => number`.
|
|
396
|
+
* @returns This container.
|
|
397
|
+
* @remarks Time O(n log n), Space O(n) (materializes to array temporarily)
|
|
398
|
+
*/
|
|
399
|
+
sort(compareFn) {
|
|
400
|
+
const arr = this.toArray();
|
|
401
|
+
arr.sort(compareFn);
|
|
402
|
+
this.clear();
|
|
403
|
+
for (const item of arr) this.push(item);
|
|
404
|
+
return this;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Remove and/or insert elements at a position (array-compatible).
|
|
408
|
+
* @param start - Start index (supports negative index).
|
|
409
|
+
* @param deleteCount - How many to remove.
|
|
410
|
+
* @param items - Elements to insert.
|
|
411
|
+
* @returns Removed elements as a new list (`this` type).
|
|
412
|
+
* @remarks Time O(n + m), Space O(min(n, m)) where `m = items.length`
|
|
413
|
+
*/
|
|
414
|
+
splice(start, deleteCount = 0, ...items) {
|
|
415
|
+
const removedList = this._createInstance();
|
|
416
|
+
start = start < 0 ? this.length + start : start;
|
|
417
|
+
start = Math.max(0, Math.min(start, this.length));
|
|
418
|
+
deleteCount = Math.max(0, Math.min(deleteCount, this.length - start));
|
|
419
|
+
for (let i = 0; i < deleteCount; i++) {
|
|
420
|
+
const removed = this.deleteAt(start);
|
|
421
|
+
if (removed !== void 0) {
|
|
422
|
+
removedList.push(removed);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
for (let i = 0; i < items.length; i++) {
|
|
426
|
+
this.addAt(start + i, items[i]);
|
|
427
|
+
}
|
|
428
|
+
return removedList;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Join all elements into a string.
|
|
432
|
+
* @param separator - Separator string.
|
|
433
|
+
* @returns Concatenated string.
|
|
434
|
+
* @remarks Time O(n), Space O(n)
|
|
435
|
+
*/
|
|
436
|
+
join(separator = ",") {
|
|
437
|
+
return this.toArray().join(separator);
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Snapshot elements into a reversed array.
|
|
441
|
+
* @returns New reversed array.
|
|
442
|
+
* @remarks Time O(n), Space O(n)
|
|
443
|
+
*/
|
|
444
|
+
toReversedArray() {
|
|
445
|
+
const array = [];
|
|
446
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
447
|
+
array.push(this.at(i));
|
|
448
|
+
}
|
|
449
|
+
return array;
|
|
450
|
+
}
|
|
451
|
+
reduceRight(callbackfn, initialValue) {
|
|
452
|
+
let accumulator = initialValue != null ? initialValue : 0;
|
|
453
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
454
|
+
accumulator = callbackfn(accumulator, this.at(i), i, this);
|
|
455
|
+
}
|
|
456
|
+
return accumulator;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Create a shallow copy of a subrange.
|
|
460
|
+
* @param start - Inclusive start (supports negative index).
|
|
461
|
+
* @param end - Exclusive end (supports negative index).
|
|
462
|
+
* @returns New list with the range (`this` type).
|
|
463
|
+
* @remarks Time O(n), Space O(n)
|
|
464
|
+
*/
|
|
465
|
+
slice(start = 0, end = this.length) {
|
|
466
|
+
start = start < 0 ? this.length + start : start;
|
|
467
|
+
end = end < 0 ? this.length + end : end;
|
|
468
|
+
const newList = this._createInstance();
|
|
469
|
+
for (let i = start; i < end; i++) {
|
|
470
|
+
newList.push(this.at(i));
|
|
471
|
+
}
|
|
472
|
+
return newList;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Fill a range with a value.
|
|
476
|
+
* @param value - Value to set.
|
|
477
|
+
* @param start - Inclusive start.
|
|
478
|
+
* @param end - Exclusive end.
|
|
479
|
+
* @returns This list.
|
|
480
|
+
* @remarks Time O(n), Space O(1)
|
|
481
|
+
*/
|
|
482
|
+
fill(value, start = 0, end = this.length) {
|
|
483
|
+
start = start < 0 ? this.length + start : start;
|
|
484
|
+
end = end < 0 ? this.length + end : end;
|
|
485
|
+
if (start < 0) start = 0;
|
|
486
|
+
if (end > this.length) end = this.length;
|
|
487
|
+
if (start >= end) return this;
|
|
488
|
+
for (let i = start; i < end; i++) {
|
|
489
|
+
this.setAt(i, value);
|
|
490
|
+
}
|
|
491
|
+
return this;
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
// src/data-structures/queue/queue.ts
|
|
496
|
+
var Queue = class _Queue extends LinearBase {
|
|
497
|
+
/**
|
|
498
|
+
* Create a Queue and optionally bulk-insert elements.
|
|
499
|
+
* @remarks Time O(N), Space O(N)
|
|
500
|
+
* @param [elements] - Iterable of elements (or raw records if toElementFn is set).
|
|
501
|
+
* @param [options] - Options such as toElementFn, maxLen, and autoCompactRatio.
|
|
502
|
+
* @returns New Queue instance.
|
|
503
|
+
*/
|
|
504
|
+
constructor(elements = [], options) {
|
|
505
|
+
super(options);
|
|
506
|
+
__publicField(this, "_elements", []);
|
|
507
|
+
__publicField(this, "_offset", 0);
|
|
508
|
+
__publicField(this, "_autoCompactRatio", 0.5);
|
|
509
|
+
if (options) {
|
|
510
|
+
const { autoCompactRatio = 0.5 } = options;
|
|
511
|
+
this._autoCompactRatio = autoCompactRatio;
|
|
512
|
+
}
|
|
513
|
+
this.pushMany(elements);
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Get the underlying array buffer.
|
|
517
|
+
* @remarks Time O(1), Space O(1)
|
|
518
|
+
* @returns Backing array of elements.
|
|
519
|
+
*/
|
|
520
|
+
get elements() {
|
|
521
|
+
return this._elements;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Get the current start offset into the array.
|
|
525
|
+
* @remarks Time O(1), Space O(1)
|
|
526
|
+
* @returns Zero-based offset.
|
|
527
|
+
*/
|
|
528
|
+
get offset() {
|
|
529
|
+
return this._offset;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Get the compaction threshold (offset/size).
|
|
533
|
+
* @remarks Time O(1), Space O(1)
|
|
534
|
+
* @returns Auto-compaction ratio in (0,1].
|
|
535
|
+
*/
|
|
536
|
+
get autoCompactRatio() {
|
|
537
|
+
return this._autoCompactRatio;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Set the compaction threshold.
|
|
541
|
+
* @remarks Time O(1), Space O(1)
|
|
542
|
+
* @param value - New ratio; compacts when offset/size exceeds this value.
|
|
543
|
+
* @returns void
|
|
544
|
+
*/
|
|
545
|
+
set autoCompactRatio(value) {
|
|
546
|
+
this._autoCompactRatio = value;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Get the number of elements currently in the queue.
|
|
550
|
+
* @remarks Time O(1), Space O(1)
|
|
551
|
+
* @returns Current length.
|
|
552
|
+
*/
|
|
553
|
+
get length() {
|
|
554
|
+
return this.elements.length - this._offset;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Get the first element (front) without removing it.
|
|
558
|
+
* @remarks Time O(1), Space O(1)
|
|
559
|
+
* @returns Front element or undefined.
|
|
560
|
+
*/
|
|
561
|
+
get first() {
|
|
562
|
+
return this.length > 0 ? this.elements[this._offset] : void 0;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Get the last element (back) without removing it.
|
|
566
|
+
* @remarks Time O(1), Space O(1)
|
|
567
|
+
* @returns Back element or undefined.
|
|
568
|
+
*/
|
|
569
|
+
get last() {
|
|
570
|
+
return this.length > 0 ? this.elements[this.elements.length - 1] : void 0;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Create a queue from an array of elements.
|
|
574
|
+
* @remarks Time O(N), Space O(N)
|
|
575
|
+
* @template E
|
|
576
|
+
* @param elements - Array of elements to enqueue in order.
|
|
577
|
+
* @returns A new queue populated from the array.
|
|
578
|
+
*/
|
|
579
|
+
static fromArray(elements) {
|
|
580
|
+
return new _Queue(elements);
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Check whether the queue is empty.
|
|
584
|
+
* @remarks Time O(1), Space O(1)
|
|
585
|
+
* @returns True if length is 0.
|
|
586
|
+
*/
|
|
587
|
+
isEmpty() {
|
|
588
|
+
return this.length === 0;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Enqueue one element at the back.
|
|
592
|
+
* @remarks Time O(1), Space O(1)
|
|
593
|
+
* @param element - Element to enqueue.
|
|
594
|
+
* @returns True on success.
|
|
595
|
+
*/
|
|
596
|
+
push(element) {
|
|
597
|
+
this.elements.push(element);
|
|
598
|
+
if (this._maxLen > 0 && this.length > this._maxLen) this.shift();
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Enqueue many elements from an iterable.
|
|
603
|
+
* @remarks Time O(N), Space O(1)
|
|
604
|
+
* @param elements - Iterable of elements (or raw records if toElementFn is set).
|
|
605
|
+
* @returns Array of per-element success flags.
|
|
606
|
+
*/
|
|
607
|
+
pushMany(elements) {
|
|
608
|
+
const ans = [];
|
|
609
|
+
for (const el of elements) {
|
|
610
|
+
if (this.toElementFn) ans.push(this.push(this.toElementFn(el)));
|
|
611
|
+
else ans.push(this.push(el));
|
|
612
|
+
}
|
|
613
|
+
return ans;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Dequeue one element from the front (amortized via offset).
|
|
617
|
+
* @remarks Time O(1) amortized, Space O(1)
|
|
618
|
+
* @returns Removed element or undefined.
|
|
619
|
+
*/
|
|
620
|
+
shift() {
|
|
621
|
+
if (this.length === 0) return void 0;
|
|
622
|
+
const first = this.first;
|
|
623
|
+
this._offset += 1;
|
|
624
|
+
if (this.elements.length > 0 && this.offset / this.elements.length > this.autoCompactRatio) this.compact();
|
|
625
|
+
return first;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Delete the first occurrence of a specific element.
|
|
629
|
+
* @remarks Time O(N), Space O(1)
|
|
630
|
+
* @param element - Element to remove (strict equality via Object.is).
|
|
631
|
+
* @returns True if an element was removed.
|
|
632
|
+
*/
|
|
633
|
+
delete(element) {
|
|
634
|
+
for (let i = this._offset; i < this.elements.length; i++) {
|
|
635
|
+
if (Object.is(this.elements[i], element)) {
|
|
636
|
+
this.elements.splice(i, 1);
|
|
637
|
+
return true;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Get the element at a given logical index.
|
|
644
|
+
* @remarks Time O(1), Space O(1)
|
|
645
|
+
* @param index - Zero-based index from the front.
|
|
646
|
+
* @returns Element or undefined.
|
|
647
|
+
*/
|
|
648
|
+
at(index) {
|
|
649
|
+
if (index < 0 || index >= this.length) return void 0;
|
|
650
|
+
return this._elements[this._offset + index];
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Delete the element at a given index.
|
|
654
|
+
* @remarks Time O(N), Space O(1)
|
|
655
|
+
* @param index - Zero-based index from the front.
|
|
656
|
+
* @returns Removed element or undefined.
|
|
657
|
+
*/
|
|
658
|
+
deleteAt(index) {
|
|
659
|
+
if (index < 0 || index >= this.length) return void 0;
|
|
660
|
+
const gi = this._offset + index;
|
|
661
|
+
const [deleted] = this.elements.splice(gi, 1);
|
|
662
|
+
return deleted;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Insert a new element at a given index.
|
|
666
|
+
* @remarks Time O(N), Space O(1)
|
|
667
|
+
* @param index - Zero-based index from the front.
|
|
668
|
+
* @param newElement - Element to insert.
|
|
669
|
+
* @returns True if inserted.
|
|
670
|
+
*/
|
|
671
|
+
addAt(index, newElement) {
|
|
672
|
+
if (index < 0 || index > this.length) return false;
|
|
673
|
+
this._elements.splice(this._offset + index, 0, newElement);
|
|
674
|
+
return true;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Replace the element at a given index.
|
|
678
|
+
* @remarks Time O(1), Space O(1)
|
|
679
|
+
* @param index - Zero-based index from the front.
|
|
680
|
+
* @param newElement - New element to set.
|
|
681
|
+
* @returns True if updated.
|
|
682
|
+
*/
|
|
683
|
+
setAt(index, newElement) {
|
|
684
|
+
if (index < 0 || index >= this.length) return false;
|
|
685
|
+
this._elements[this._offset + index] = newElement;
|
|
686
|
+
return true;
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Reverse the queue in-place by compacting then reversing.
|
|
690
|
+
* @remarks Time O(N), Space O(N)
|
|
691
|
+
* @returns This queue.
|
|
692
|
+
*/
|
|
693
|
+
reverse() {
|
|
694
|
+
this._elements = this.elements.slice(this._offset).reverse();
|
|
695
|
+
this._offset = 0;
|
|
696
|
+
return this;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Remove all elements and reset offset.
|
|
700
|
+
* @remarks Time O(1), Space O(1)
|
|
701
|
+
* @returns void
|
|
702
|
+
*/
|
|
703
|
+
clear() {
|
|
704
|
+
this._elements = [];
|
|
705
|
+
this._offset = 0;
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Compact storage by discarding consumed head elements.
|
|
709
|
+
* @remarks Time O(N), Space O(N)
|
|
710
|
+
* @returns True when compaction performed.
|
|
711
|
+
*/
|
|
712
|
+
compact() {
|
|
713
|
+
this._elements = this.elements.slice(this._offset);
|
|
714
|
+
this._offset = 0;
|
|
715
|
+
return true;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Remove and/or insert elements at a position (array-like).
|
|
719
|
+
* @remarks Time O(N + M), Space O(M)
|
|
720
|
+
* @param start - Start index (clamped to [0, length]).
|
|
721
|
+
* @param [deleteCount] - Number of elements to remove (default 0).
|
|
722
|
+
* @param [items] - Elements to insert after `start`.
|
|
723
|
+
* @returns A new queue containing the removed elements (typed as `this`).
|
|
724
|
+
*/
|
|
725
|
+
splice(start, deleteCount = 0, ...items) {
|
|
726
|
+
start = Math.max(0, Math.min(start, this.length));
|
|
727
|
+
deleteCount = Math.max(0, Math.min(deleteCount, this.length - start));
|
|
728
|
+
const gi = this._offset + start;
|
|
729
|
+
const removedArray = this._elements.splice(gi, deleteCount, ...items);
|
|
730
|
+
if (this.elements.length > 0 && this.offset / this.elements.length > this.autoCompactRatio) this.compact();
|
|
731
|
+
const removed = this._createInstance({ toElementFn: this.toElementFn, maxLen: this._maxLen });
|
|
732
|
+
removed._setAutoCompactRatio(this._autoCompactRatio);
|
|
733
|
+
removed.pushMany(removedArray);
|
|
734
|
+
return removed;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Deep clone this queue and its parameters.
|
|
738
|
+
* @remarks Time O(N), Space O(N)
|
|
739
|
+
* @returns A new queue with the same content and options.
|
|
740
|
+
*/
|
|
741
|
+
clone() {
|
|
742
|
+
const out = this._createInstance({ toElementFn: this.toElementFn, maxLen: this._maxLen });
|
|
743
|
+
out._setAutoCompactRatio(this._autoCompactRatio);
|
|
744
|
+
for (let i = this._offset; i < this.elements.length; i++) out.push(this.elements[i]);
|
|
745
|
+
return out;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Filter elements into a new queue of the same class.
|
|
749
|
+
* @remarks Time O(N), Space O(N)
|
|
750
|
+
* @param predicate - Predicate (element, index, queue) → boolean to keep element.
|
|
751
|
+
* @param [thisArg] - Value for `this` inside the predicate.
|
|
752
|
+
* @returns A new queue with kept elements.
|
|
753
|
+
*/
|
|
754
|
+
filter(predicate, thisArg) {
|
|
755
|
+
const out = this._createInstance({ toElementFn: this.toElementFn, maxLen: this._maxLen });
|
|
756
|
+
out._setAutoCompactRatio(this._autoCompactRatio);
|
|
757
|
+
let index = 0;
|
|
758
|
+
for (const v of this) {
|
|
759
|
+
if (predicate.call(thisArg, v, index, this)) out.push(v);
|
|
760
|
+
index++;
|
|
761
|
+
}
|
|
762
|
+
return out;
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Map each element to a new element in a possibly different-typed queue.
|
|
766
|
+
* @remarks Time O(N), Space O(N)
|
|
767
|
+
* @template EM
|
|
768
|
+
* @template RM
|
|
769
|
+
* @param callback - Mapping function (element, index, queue) → newElement.
|
|
770
|
+
* @param [options] - Options for the output queue (e.g., toElementFn, maxLen, autoCompactRatio).
|
|
771
|
+
* @param [thisArg] - Value for `this` inside the callback.
|
|
772
|
+
* @returns A new Queue with mapped elements.
|
|
773
|
+
*/
|
|
774
|
+
map(callback, options, thisArg) {
|
|
775
|
+
var _a, _b;
|
|
776
|
+
const out = new this.constructor([], {
|
|
777
|
+
toElementFn: options == null ? void 0 : options.toElementFn,
|
|
778
|
+
maxLen: (_a = options == null ? void 0 : options.maxLen) != null ? _a : this._maxLen,
|
|
779
|
+
autoCompactRatio: (_b = options == null ? void 0 : options.autoCompactRatio) != null ? _b : this._autoCompactRatio
|
|
780
|
+
});
|
|
781
|
+
let index = 0;
|
|
782
|
+
for (const v of this)
|
|
783
|
+
out.push(thisArg === void 0 ? callback(v, index++, this) : callback.call(thisArg, v, index++, this));
|
|
784
|
+
return out;
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Map each element to a new value of the same type.
|
|
788
|
+
* @remarks Time O(N), Space O(N)
|
|
789
|
+
* @param callback - Mapping function (element, index, queue) → element.
|
|
790
|
+
* @param [thisArg] - Value for `this` inside the callback.
|
|
791
|
+
* @returns A new queue with mapped elements (same element type).
|
|
792
|
+
*/
|
|
793
|
+
mapSame(callback, thisArg) {
|
|
794
|
+
var _a;
|
|
795
|
+
const Ctor = this.constructor;
|
|
796
|
+
const out = new Ctor([], {
|
|
797
|
+
toElementFn: this.toElementFn,
|
|
798
|
+
maxLen: this._maxLen,
|
|
799
|
+
autoCompactRatio: this._autoCompactRatio
|
|
800
|
+
});
|
|
801
|
+
(_a = out._setAutoCompactRatio) == null ? void 0 : _a.call(out, this._autoCompactRatio);
|
|
802
|
+
let index = 0;
|
|
803
|
+
for (const v of this) {
|
|
804
|
+
const mv = thisArg === void 0 ? callback(v, index++, this) : callback.call(thisArg, v, index++, this);
|
|
805
|
+
out.push(mv);
|
|
806
|
+
}
|
|
807
|
+
return out;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* (Protected) Set the internal auto-compaction ratio.
|
|
811
|
+
* @remarks Time O(1), Space O(1)
|
|
812
|
+
* @param value - New ratio to assign.
|
|
813
|
+
* @returns void
|
|
814
|
+
*/
|
|
815
|
+
_setAutoCompactRatio(value) {
|
|
816
|
+
this._autoCompactRatio = value;
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* (Protected) Iterate elements from front to back.
|
|
820
|
+
* @remarks Time O(N), Space O(1)
|
|
821
|
+
* @returns Iterator of E.
|
|
822
|
+
*/
|
|
823
|
+
*_getIterator() {
|
|
824
|
+
for (let i = this._offset; i < this.elements.length; i++) yield this.elements[i];
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* (Protected) Iterate elements from back to front.
|
|
828
|
+
* @remarks Time O(N), Space O(1)
|
|
829
|
+
* @returns Iterator of E.
|
|
830
|
+
*/
|
|
831
|
+
*_getReverseIterator() {
|
|
832
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
833
|
+
const cur = this.at(i);
|
|
834
|
+
if (cur !== void 0) yield cur;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* (Protected) Create an empty instance of the same concrete class.
|
|
839
|
+
* @remarks Time O(1), Space O(1)
|
|
840
|
+
* @param [options] - Options forwarded to the constructor.
|
|
841
|
+
* @returns An empty like-kind queue instance.
|
|
842
|
+
*/
|
|
843
|
+
_createInstance(options) {
|
|
844
|
+
const Ctor = this.constructor;
|
|
845
|
+
return new Ctor([], options);
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* (Protected) Create a like-kind queue and seed it from an iterable.
|
|
849
|
+
* @remarks Time O(N), Space O(N)
|
|
850
|
+
* @template EM
|
|
851
|
+
* @template RM
|
|
852
|
+
* @param [elements] - Iterable used to seed the new queue.
|
|
853
|
+
* @param [options] - Options forwarded to the constructor.
|
|
854
|
+
* @returns A like-kind Queue instance.
|
|
855
|
+
*/
|
|
856
|
+
_createLike(elements = [], options) {
|
|
857
|
+
const Ctor = this.constructor;
|
|
858
|
+
return new Ctor(elements, options);
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
// src/data-structures/base/iterable-entry-base.ts
|
|
863
|
+
var IterableEntryBase = class {
|
|
864
|
+
/**
|
|
865
|
+
* Default iterator yielding `[key, value]` entries.
|
|
866
|
+
* @returns Iterator of `[K, V]`.
|
|
867
|
+
* @remarks Time O(n) to iterate, Space O(1)
|
|
868
|
+
*/
|
|
869
|
+
*[Symbol.iterator](...args) {
|
|
870
|
+
yield* this._getIterator(...args);
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Iterate over `[key, value]` pairs (may yield `undefined` values).
|
|
874
|
+
* @returns Iterator of `[K, V | undefined]`.
|
|
875
|
+
* @remarks Time O(n), Space O(1)
|
|
876
|
+
*/
|
|
877
|
+
*entries() {
|
|
878
|
+
for (const item of this) {
|
|
879
|
+
yield item;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Iterate over keys only.
|
|
884
|
+
* @returns Iterator of keys.
|
|
885
|
+
* @remarks Time O(n), Space O(1)
|
|
886
|
+
*/
|
|
887
|
+
*keys() {
|
|
888
|
+
for (const item of this) {
|
|
889
|
+
yield item[0];
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Iterate over values only.
|
|
894
|
+
* @returns Iterator of values.
|
|
895
|
+
* @remarks Time O(n), Space O(1)
|
|
896
|
+
*/
|
|
897
|
+
*values() {
|
|
898
|
+
for (const item of this) {
|
|
899
|
+
yield item[1];
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Test whether all entries satisfy the predicate.
|
|
904
|
+
* @param predicate - `(key, value, index, self) => boolean`.
|
|
905
|
+
* @param thisArg - Optional `this` for callback.
|
|
906
|
+
* @returns `true` if all pass; otherwise `false`.
|
|
907
|
+
* @remarks Time O(n), Space O(1)
|
|
908
|
+
*/
|
|
909
|
+
every(predicate, thisArg) {
|
|
910
|
+
let index = 0;
|
|
911
|
+
for (const item of this) {
|
|
912
|
+
if (!predicate.call(thisArg, item[0], item[1], index++, this)) {
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
return true;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Test whether any entry satisfies the predicate.
|
|
920
|
+
* @param predicate - `(key, value, index, self) => boolean`.
|
|
921
|
+
* @param thisArg - Optional `this` for callback.
|
|
922
|
+
* @returns `true` if any passes; otherwise `false`.
|
|
923
|
+
* @remarks Time O(n), Space O(1)
|
|
924
|
+
*/
|
|
925
|
+
some(predicate, thisArg) {
|
|
926
|
+
let index = 0;
|
|
927
|
+
for (const item of this) {
|
|
928
|
+
if (predicate.call(thisArg, item[0], item[1], index++, this)) {
|
|
929
|
+
return true;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Visit each entry, left-to-right.
|
|
936
|
+
* @param callbackfn - `(key, value, index, self) => void`.
|
|
937
|
+
* @param thisArg - Optional `this` for callback.
|
|
938
|
+
* @remarks Time O(n), Space O(1)
|
|
939
|
+
*/
|
|
940
|
+
forEach(callbackfn, thisArg) {
|
|
941
|
+
let index = 0;
|
|
942
|
+
for (const item of this) {
|
|
943
|
+
const [key, value] = item;
|
|
944
|
+
callbackfn.call(thisArg, key, value, index++, this);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Find the first entry that matches a predicate.
|
|
949
|
+
* @param callbackfn - `(key, value, index, self) => boolean`.
|
|
950
|
+
* @param thisArg - Optional `this` for callback.
|
|
951
|
+
* @returns Matching `[key, value]` or `undefined`.
|
|
952
|
+
* @remarks Time O(n), Space O(1)
|
|
953
|
+
*/
|
|
954
|
+
find(callbackfn, thisArg) {
|
|
955
|
+
let index = 0;
|
|
956
|
+
for (const item of this) {
|
|
957
|
+
const [key, value] = item;
|
|
958
|
+
if (callbackfn.call(thisArg, key, value, index++, this)) return item;
|
|
959
|
+
}
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Whether the given key exists.
|
|
964
|
+
* @param key - Key to test.
|
|
965
|
+
* @returns `true` if found; otherwise `false`.
|
|
966
|
+
* @remarks Time O(n) generic, Space O(1)
|
|
967
|
+
*/
|
|
968
|
+
has(key) {
|
|
969
|
+
for (const item of this) {
|
|
970
|
+
const [itemKey] = item;
|
|
971
|
+
if (itemKey === key) return true;
|
|
972
|
+
}
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Whether there exists an entry with the given value.
|
|
977
|
+
* @param value - Value to test.
|
|
978
|
+
* @returns `true` if found; otherwise `false`.
|
|
979
|
+
* @remarks Time O(n), Space O(1)
|
|
980
|
+
*/
|
|
981
|
+
hasValue(value) {
|
|
982
|
+
for (const [, elementValue] of this) {
|
|
983
|
+
if (elementValue === value) return true;
|
|
984
|
+
}
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Get the value under a key.
|
|
989
|
+
* @param key - Key to look up.
|
|
990
|
+
* @returns Value or `undefined`.
|
|
991
|
+
* @remarks Time O(n) generic, Space O(1)
|
|
992
|
+
*/
|
|
993
|
+
get(key) {
|
|
994
|
+
for (const item of this) {
|
|
995
|
+
const [itemKey, value] = item;
|
|
996
|
+
if (itemKey === key) return value;
|
|
997
|
+
}
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Reduce entries into a single accumulator.
|
|
1002
|
+
* @param callbackfn - `(acc, value, key, index, self) => acc`.
|
|
1003
|
+
* @param initialValue - Initial accumulator.
|
|
1004
|
+
* @returns Final accumulator.
|
|
1005
|
+
* @remarks Time O(n), Space O(1)
|
|
1006
|
+
*/
|
|
1007
|
+
reduce(callbackfn, initialValue) {
|
|
1008
|
+
let accumulator = initialValue;
|
|
1009
|
+
let index = 0;
|
|
1010
|
+
for (const item of this) {
|
|
1011
|
+
const [key, value] = item;
|
|
1012
|
+
accumulator = callbackfn(accumulator, value, key, index++, this);
|
|
1013
|
+
}
|
|
1014
|
+
return accumulator;
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Visualize the iterable as an array of `[key, value]` pairs (or a custom string).
|
|
1018
|
+
* @returns Array of entries (default) or a string.
|
|
1019
|
+
* @remarks Time O(n), Space O(n)
|
|
1020
|
+
*/
|
|
1021
|
+
toVisual() {
|
|
1022
|
+
return [...this];
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Print a human-friendly representation to the console.
|
|
1026
|
+
* @remarks Time O(n), Space O(n)
|
|
1027
|
+
*/
|
|
1028
|
+
print() {
|
|
1029
|
+
console.log(this.toVisual());
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
|
|
1033
|
+
// src/common/index.ts
|
|
1034
|
+
var DFSOperation = /* @__PURE__ */ ((DFSOperation2) => {
|
|
1035
|
+
DFSOperation2[DFSOperation2["VISIT"] = 0] = "VISIT";
|
|
1036
|
+
DFSOperation2[DFSOperation2["PROCESS"] = 1] = "PROCESS";
|
|
1037
|
+
return DFSOperation2;
|
|
1038
|
+
})(DFSOperation || {});
|
|
1039
|
+
var Range = class {
|
|
1040
|
+
constructor(low, high, includeLow = true, includeHigh = true) {
|
|
1041
|
+
this.low = low;
|
|
1042
|
+
this.high = high;
|
|
1043
|
+
this.includeLow = includeLow;
|
|
1044
|
+
this.includeHigh = includeHigh;
|
|
1045
|
+
if (!(isComparable(low) && isComparable(high))) throw new RangeError("low or high is not comparable");
|
|
1046
|
+
if (low > high) throw new RangeError("low must be less than or equal to high");
|
|
1047
|
+
}
|
|
1048
|
+
// Determine whether a key is within the range
|
|
1049
|
+
isInRange(key, comparator) {
|
|
1050
|
+
const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0;
|
|
1051
|
+
const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0;
|
|
1052
|
+
return lowCheck && highCheck;
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
|
|
1056
|
+
// src/data-structures/binary-tree/binary-tree.ts
|
|
1057
|
+
var BinaryTreeNode = class {
|
|
1058
|
+
/**
|
|
1059
|
+
* Creates an instance of BinaryTreeNode.
|
|
1060
|
+
* @remarks Time O(1), Space O(1)
|
|
1061
|
+
*
|
|
1062
|
+
* @param key - The key of the node.
|
|
1063
|
+
* @param [value] - The value associated with the key.
|
|
1064
|
+
*/
|
|
1065
|
+
constructor(key, value) {
|
|
1066
|
+
__publicField(this, "key");
|
|
1067
|
+
__publicField(this, "value");
|
|
1068
|
+
__publicField(this, "parent");
|
|
1069
|
+
__publicField(this, "_left");
|
|
1070
|
+
__publicField(this, "_right");
|
|
1071
|
+
__publicField(this, "_height", 0);
|
|
1072
|
+
__publicField(this, "_color", "BLACK");
|
|
1073
|
+
__publicField(this, "_count", 1);
|
|
1074
|
+
this.key = key;
|
|
1075
|
+
this.value = value;
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Gets the left child of the node.
|
|
1079
|
+
* @remarks Time O(1), Space O(1)
|
|
1080
|
+
*
|
|
1081
|
+
* @returns The left child.
|
|
1082
|
+
*/
|
|
1083
|
+
get left() {
|
|
1084
|
+
return this._left;
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Sets the left child of the node and updates its parent reference.
|
|
1088
|
+
* @remarks Time O(1), Space O(1)
|
|
1089
|
+
*
|
|
1090
|
+
* @param v - The node to set as the left child.
|
|
1091
|
+
*/
|
|
1092
|
+
set left(v) {
|
|
1093
|
+
if (v) {
|
|
1094
|
+
v.parent = this;
|
|
1095
|
+
}
|
|
1096
|
+
this._left = v;
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Gets the right child of the node.
|
|
1100
|
+
* @remarks Time O(1), Space O(1)
|
|
1101
|
+
*
|
|
1102
|
+
* @returns The right child.
|
|
1103
|
+
*/
|
|
1104
|
+
get right() {
|
|
1105
|
+
return this._right;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Sets the right child of the node and updates its parent reference.
|
|
1109
|
+
* @remarks Time O(1), Space O(1)
|
|
1110
|
+
*
|
|
1111
|
+
* @param v - The node to set as the right child.
|
|
1112
|
+
*/
|
|
1113
|
+
set right(v) {
|
|
1114
|
+
if (v) {
|
|
1115
|
+
v.parent = this;
|
|
1116
|
+
}
|
|
1117
|
+
this._right = v;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Gets the height of the node (used in self-balancing trees).
|
|
1121
|
+
* @remarks Time O(1), Space O(1)
|
|
1122
|
+
*
|
|
1123
|
+
* @returns The height.
|
|
1124
|
+
*/
|
|
1125
|
+
get height() {
|
|
1126
|
+
return this._height;
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Sets the height of the node.
|
|
1130
|
+
* @remarks Time O(1), Space O(1)
|
|
1131
|
+
*
|
|
1132
|
+
* @param value - The new height.
|
|
1133
|
+
*/
|
|
1134
|
+
set height(value) {
|
|
1135
|
+
this._height = value;
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Gets the color of the node (used in Red-Black trees).
|
|
1139
|
+
* @remarks Time O(1), Space O(1)
|
|
1140
|
+
*
|
|
1141
|
+
* @returns The node's color.
|
|
1142
|
+
*/
|
|
1143
|
+
get color() {
|
|
1144
|
+
return this._color;
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Sets the color of the node.
|
|
1148
|
+
* @remarks Time O(1), Space O(1)
|
|
1149
|
+
*
|
|
1150
|
+
* @param value - The new color.
|
|
1151
|
+
*/
|
|
1152
|
+
set color(value) {
|
|
1153
|
+
this._color = value;
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Gets the count of nodes in the subtree rooted at this node (used in order-statistic trees).
|
|
1157
|
+
* @remarks Time O(1), Space O(1)
|
|
1158
|
+
*
|
|
1159
|
+
* @returns The subtree node count.
|
|
1160
|
+
*/
|
|
1161
|
+
get count() {
|
|
1162
|
+
return this._count;
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Sets the count of nodes in the subtree.
|
|
1166
|
+
* @remarks Time O(1), Space O(1)
|
|
1167
|
+
*
|
|
1168
|
+
* @param value - The new count.
|
|
1169
|
+
*/
|
|
1170
|
+
set count(value) {
|
|
1171
|
+
this._count = value;
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Gets the position of the node relative to its parent.
|
|
1175
|
+
* @remarks Time O(1), Space O(1)
|
|
1176
|
+
*
|
|
1177
|
+
* @returns The family position (e.g., 'ROOT', 'LEFT', 'RIGHT').
|
|
1178
|
+
*/
|
|
1179
|
+
get familyPosition() {
|
|
1180
|
+
if (!this.parent) {
|
|
1181
|
+
return this.left || this.right ? "ROOT" : "ISOLATED";
|
|
1182
|
+
}
|
|
1183
|
+
if (this.parent.left === this) {
|
|
1184
|
+
return this.left || this.right ? "ROOT_LEFT" : "LEFT";
|
|
1185
|
+
} else if (this.parent.right === this) {
|
|
1186
|
+
return this.left || this.right ? "ROOT_RIGHT" : "RIGHT";
|
|
1187
|
+
}
|
|
1188
|
+
return "MAL_NODE";
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
var BinaryTree = class extends IterableEntryBase {
|
|
1192
|
+
/**
|
|
1193
|
+
* Creates an instance of BinaryTree.
|
|
1194
|
+
* @remarks Time O(N * M), where N is the number of items in `keysNodesEntriesOrRaws` and M is the tree size at insertion time (due to O(M) `add` operation). Space O(N) for storing the nodes.
|
|
1195
|
+
*
|
|
1196
|
+
* @param [keysNodesEntriesOrRaws=[]] - An iterable of items to add.
|
|
1197
|
+
* @param [options] - Configuration options for the tree.
|
|
1198
|
+
*/
|
|
1199
|
+
constructor(keysNodesEntriesOrRaws = [], options) {
|
|
1200
|
+
super();
|
|
1201
|
+
__publicField(this, "iterationType", "ITERATIVE");
|
|
1202
|
+
__publicField(this, "_isMapMode", true);
|
|
1203
|
+
__publicField(this, "_isDuplicate", false);
|
|
1204
|
+
__publicField(this, "_store", /* @__PURE__ */ new Map());
|
|
1205
|
+
__publicField(this, "_root");
|
|
1206
|
+
__publicField(this, "_size", 0);
|
|
1207
|
+
__publicField(this, "_NIL", new BinaryTreeNode(NaN));
|
|
1208
|
+
__publicField(this, "_toEntryFn");
|
|
1209
|
+
/**
|
|
1210
|
+
* (Protected) Default callback function, returns the node's key.
|
|
1211
|
+
* @remarks Time O(1)
|
|
1212
|
+
*
|
|
1213
|
+
* @param node - The node.
|
|
1214
|
+
* @returns The node's key or undefined.
|
|
1215
|
+
*/
|
|
1216
|
+
__publicField(this, "_DEFAULT_NODE_CALLBACK", (node) => node ? node.key : void 0);
|
|
1217
|
+
if (options) {
|
|
1218
|
+
const { iterationType, toEntryFn, isMapMode, isDuplicate } = options;
|
|
1219
|
+
if (iterationType) this.iterationType = iterationType;
|
|
1220
|
+
if (isMapMode !== void 0) this._isMapMode = isMapMode;
|
|
1221
|
+
if (isDuplicate !== void 0) this._isDuplicate = isDuplicate;
|
|
1222
|
+
if (typeof toEntryFn === "function") this._toEntryFn = toEntryFn;
|
|
1223
|
+
else if (toEntryFn) throw TypeError("toEntryFn must be a function type");
|
|
1224
|
+
}
|
|
1225
|
+
if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Gets whether the tree is in Map mode.
|
|
1229
|
+
* @remarks In Map mode (default), values are stored in an external Map, and nodes only hold keys. If false, values are stored directly on the nodes. Time O(1)
|
|
1230
|
+
*
|
|
1231
|
+
* @returns True if in Map mode, false otherwise.
|
|
1232
|
+
*/
|
|
1233
|
+
get isMapMode() {
|
|
1234
|
+
return this._isMapMode;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Gets whether the tree allows duplicate keys.
|
|
1238
|
+
* @remarks Time O(1)
|
|
1239
|
+
*
|
|
1240
|
+
* @returns True if duplicates are allowed, false otherwise.
|
|
1241
|
+
*/
|
|
1242
|
+
get isDuplicate() {
|
|
1243
|
+
return this._isDuplicate;
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Gets the external value store (used in Map mode).
|
|
1247
|
+
* @remarks Time O(1)
|
|
1248
|
+
*
|
|
1249
|
+
* @returns The map storing key-value pairs.
|
|
1250
|
+
*/
|
|
1251
|
+
get store() {
|
|
1252
|
+
return this._store;
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Gets the root node of the tree.
|
|
1256
|
+
* @remarks Time O(1)
|
|
1257
|
+
*
|
|
1258
|
+
* @returns The root node.
|
|
1259
|
+
*/
|
|
1260
|
+
get root() {
|
|
1261
|
+
return this._root;
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Gets the number of nodes in the tree.
|
|
1265
|
+
* @remarks Time O(1)
|
|
1266
|
+
*
|
|
1267
|
+
* @returns The size of the tree.
|
|
1268
|
+
*/
|
|
1269
|
+
get size() {
|
|
1270
|
+
return this._size;
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Gets the sentinel NIL node (used in self-balancing trees like Red-Black Tree).
|
|
1274
|
+
* @remarks Time O(1)
|
|
1275
|
+
*
|
|
1276
|
+
* @returns The NIL node.
|
|
1277
|
+
*/
|
|
1278
|
+
get NIL() {
|
|
1279
|
+
return this._NIL;
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Gets the function used to convert raw data objects (R) into [key, value] entries.
|
|
1283
|
+
* @remarks Time O(1)
|
|
1284
|
+
*
|
|
1285
|
+
* @returns The conversion function.
|
|
1286
|
+
*/
|
|
1287
|
+
get toEntryFn() {
|
|
1288
|
+
return this._toEntryFn;
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* (Protected) Creates a new node.
|
|
1292
|
+
* @remarks Time O(1), Space O(1)
|
|
1293
|
+
*
|
|
1294
|
+
* @param key - The key for the new node.
|
|
1295
|
+
* @param [value] - The value for the new node (used if not in Map mode).
|
|
1296
|
+
* @returns The newly created node.
|
|
1297
|
+
*/
|
|
1298
|
+
createNode(key, value) {
|
|
1299
|
+
return new BinaryTreeNode(key, this._isMapMode ? void 0 : value);
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Creates a new, empty tree of the same type and configuration.
|
|
1303
|
+
* @remarks Time O(1) (excluding options cloning), Space O(1)
|
|
1304
|
+
*
|
|
1305
|
+
* @param [options] - Optional overrides for the new tree's options.
|
|
1306
|
+
* @returns A new, empty tree instance.
|
|
1307
|
+
*/
|
|
1308
|
+
createTree(options) {
|
|
1309
|
+
return this._createInstance(options);
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Ensures the input is a node. If it's a key or entry, it searches for the node.
|
|
1313
|
+
* @remarks Time O(1) if a node is passed. O(N) if a key or entry is passed (due to `getNode` performing a full search). Space O(1) if iterative search, O(H) if recursive (where H is height, O(N) worst-case).
|
|
1314
|
+
*
|
|
1315
|
+
* @param keyNodeOrEntry - The item to resolve to a node.
|
|
1316
|
+
* @param [iterationType=this.iterationType] - The traversal method to use if searching.
|
|
1317
|
+
* @returns The resolved node, or null/undefined if not found or input is null/undefined.
|
|
1318
|
+
*/
|
|
1319
|
+
ensureNode(keyNodeOrEntry, iterationType = this.iterationType) {
|
|
1320
|
+
if (keyNodeOrEntry === null) return null;
|
|
1321
|
+
if (keyNodeOrEntry === void 0) return;
|
|
1322
|
+
if (keyNodeOrEntry === this._NIL) return;
|
|
1323
|
+
if (this.isNode(keyNodeOrEntry)) return keyNodeOrEntry;
|
|
1324
|
+
if (this.isEntry(keyNodeOrEntry)) {
|
|
1325
|
+
const key = keyNodeOrEntry[0];
|
|
1326
|
+
if (key === null) return null;
|
|
1327
|
+
if (key === void 0) return;
|
|
1328
|
+
return this.getNode(key, this._root, iterationType);
|
|
1329
|
+
}
|
|
1330
|
+
return this.getNode(keyNodeOrEntry, this._root, iterationType);
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* Checks if the given item is a `BinaryTreeNode` instance.
|
|
1334
|
+
* @remarks Time O(1), Space O(1)
|
|
1335
|
+
*
|
|
1336
|
+
* @param keyNodeOrEntry - The item to check.
|
|
1337
|
+
* @returns True if it's a node, false otherwise.
|
|
1338
|
+
*/
|
|
1339
|
+
isNode(keyNodeOrEntry) {
|
|
1340
|
+
return keyNodeOrEntry instanceof BinaryTreeNode;
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Checks if the given item is a raw data object (R) that needs conversion via `toEntryFn`.
|
|
1344
|
+
* @remarks Time O(1), Space O(1)
|
|
1345
|
+
*
|
|
1346
|
+
* @param keyNodeEntryOrRaw - The item to check.
|
|
1347
|
+
* @returns True if it's a raw object, false otherwise.
|
|
1348
|
+
*/
|
|
1349
|
+
isRaw(keyNodeEntryOrRaw) {
|
|
1350
|
+
return this._toEntryFn !== void 0 && typeof keyNodeEntryOrRaw === "object";
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Checks if the given item is a "real" node (i.e., not null, undefined, or NIL).
|
|
1354
|
+
* @remarks Time O(1), Space O(1)
|
|
1355
|
+
*
|
|
1356
|
+
* @param keyNodeOrEntry - The item to check.
|
|
1357
|
+
* @returns True if it's a real node, false otherwise.
|
|
1358
|
+
*/
|
|
1359
|
+
isRealNode(keyNodeOrEntry) {
|
|
1360
|
+
if (keyNodeOrEntry === this._NIL || keyNodeOrEntry === null || keyNodeOrEntry === void 0) return false;
|
|
1361
|
+
return this.isNode(keyNodeOrEntry);
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Checks if the given item is either a "real" node or null.
|
|
1365
|
+
* @remarks Time O(1), Space O(1)
|
|
1366
|
+
*
|
|
1367
|
+
* @param keyNodeOrEntry - The item to check.
|
|
1368
|
+
* @returns True if it's a real node or null, false otherwise.
|
|
1369
|
+
*/
|
|
1370
|
+
isRealNodeOrNull(keyNodeOrEntry) {
|
|
1371
|
+
return keyNodeOrEntry === null || this.isRealNode(keyNodeOrEntry);
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Checks if the given item is the sentinel NIL node.
|
|
1375
|
+
* @remarks Time O(1), Space O(1)
|
|
1376
|
+
*
|
|
1377
|
+
* @param keyNodeOrEntry - The item to check.
|
|
1378
|
+
* @returns True if it's the NIL node, false otherwise.
|
|
1379
|
+
*/
|
|
1380
|
+
isNIL(keyNodeOrEntry) {
|
|
1381
|
+
return keyNodeOrEntry === this._NIL;
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Checks if the given item is a `Range` object.
|
|
1385
|
+
* @remarks Time O(1), Space O(1)
|
|
1386
|
+
*
|
|
1387
|
+
* @param keyNodeEntryOrPredicate - The item to check.
|
|
1388
|
+
* @returns True if it's a Range, false otherwise.
|
|
1389
|
+
*/
|
|
1390
|
+
isRange(keyNodeEntryOrPredicate) {
|
|
1391
|
+
return keyNodeEntryOrPredicate instanceof Range;
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Checks if a node is a leaf (has no real children).
|
|
1395
|
+
* @remarks Time O(N) if a key/entry is passed (due to `ensureNode`). O(1) if a node is passed. Space O(1) or O(H) (from `ensureNode`).
|
|
1396
|
+
*
|
|
1397
|
+
* @param keyNodeOrEntry - The node to check.
|
|
1398
|
+
* @returns True if the node is a leaf, false otherwise.
|
|
1399
|
+
*/
|
|
1400
|
+
isLeaf(keyNodeOrEntry) {
|
|
1401
|
+
keyNodeOrEntry = this.ensureNode(keyNodeOrEntry);
|
|
1402
|
+
if (keyNodeOrEntry === void 0) return false;
|
|
1403
|
+
if (keyNodeOrEntry === null) return true;
|
|
1404
|
+
return !this.isRealNode(keyNodeOrEntry.left) && !this.isRealNode(keyNodeOrEntry.right);
|
|
1405
|
+
}
|
|
1406
|
+
/**
|
|
1407
|
+
* Checks if the given item is a [key, value] entry pair.
|
|
1408
|
+
* @remarks Time O(1), Space O(1)
|
|
1409
|
+
*
|
|
1410
|
+
* @param keyNodeOrEntry - The item to check.
|
|
1411
|
+
* @returns True if it's an entry, false otherwise.
|
|
1412
|
+
*/
|
|
1413
|
+
isEntry(keyNodeOrEntry) {
|
|
1414
|
+
return Array.isArray(keyNodeOrEntry) && keyNodeOrEntry.length === 2;
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Checks if the given key is valid (comparable or null).
|
|
1418
|
+
* @remarks Time O(1), Space O(1)
|
|
1419
|
+
*
|
|
1420
|
+
* @param key - The key to validate.
|
|
1421
|
+
* @returns True if the key is valid, false otherwise.
|
|
1422
|
+
*/
|
|
1423
|
+
isValidKey(key) {
|
|
1424
|
+
if (key === null) return true;
|
|
1425
|
+
return isComparable(key);
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Adds a new node to the tree.
|
|
1429
|
+
* @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). This implementation adds the node at the first available position in a level-order (BFS) traversal. This is NOT a Binary Search Tree insertion. Time O(N), where N is the number of nodes. It must traverse level-by-level to find an empty slot. Space O(N) in the worst case for the BFS queue (e.g., a full last level).
|
|
1430
|
+
*
|
|
1431
|
+
* @param keyNodeOrEntry - The key, node, or entry to add.
|
|
1432
|
+
* @param [value] - The value, if providing just a key.
|
|
1433
|
+
* @returns True if the addition was successful, false otherwise.
|
|
1434
|
+
*/
|
|
1435
|
+
add(keyNodeOrEntry, value) {
|
|
1436
|
+
const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
|
1437
|
+
if (newNode === void 0) return false;
|
|
1438
|
+
if (!this._root) {
|
|
1439
|
+
this._setRoot(newNode);
|
|
1440
|
+
if (this._isMapMode) this._setValue(newNode == null ? void 0 : newNode.key, newValue);
|
|
1441
|
+
this._size = 1;
|
|
1442
|
+
return true;
|
|
1443
|
+
}
|
|
1444
|
+
const queue = new Queue([this._root]);
|
|
1445
|
+
let potentialParent;
|
|
1446
|
+
while (queue.length > 0) {
|
|
1447
|
+
const cur = queue.shift();
|
|
1448
|
+
if (!cur) continue;
|
|
1449
|
+
if (!this._isDuplicate) {
|
|
1450
|
+
if (newNode !== null && cur.key === newNode.key) {
|
|
1451
|
+
this._replaceNode(cur, newNode);
|
|
1452
|
+
if (this._isMapMode) this._setValue(cur.key, newValue);
|
|
1453
|
+
return true;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
if (potentialParent === void 0 && (cur.left === void 0 || cur.right === void 0)) {
|
|
1457
|
+
potentialParent = cur;
|
|
1458
|
+
}
|
|
1459
|
+
if (cur.left !== null) {
|
|
1460
|
+
if (cur.left) queue.push(cur.left);
|
|
1461
|
+
}
|
|
1462
|
+
if (cur.right !== null) {
|
|
1463
|
+
if (cur.right) queue.push(cur.right);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
if (potentialParent) {
|
|
1467
|
+
if (potentialParent.left === void 0) {
|
|
1468
|
+
potentialParent.left = newNode;
|
|
1469
|
+
} else if (potentialParent.right === void 0) {
|
|
1470
|
+
potentialParent.right = newNode;
|
|
1471
|
+
}
|
|
1472
|
+
if (this._isMapMode) this._setValue(newNode == null ? void 0 : newNode.key, newValue);
|
|
1473
|
+
this._size++;
|
|
1474
|
+
return true;
|
|
1475
|
+
}
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Adds multiple items to the tree.
|
|
1480
|
+
* @remarks Time O(N * M), where N is the number of items to add and M is the size of the tree at insertion (due to O(M) `add` operation). Space O(M) (from `add`) + O(N) (for the `inserted` array).
|
|
1481
|
+
*
|
|
1482
|
+
* @param keysNodesEntriesOrRaws - An iterable of items to add.
|
|
1483
|
+
* @param [values] - An optional parallel iterable of values.
|
|
1484
|
+
* @returns An array of booleans indicating the success of each individual `add` operation.
|
|
1485
|
+
*/
|
|
1486
|
+
addMany(keysNodesEntriesOrRaws, values) {
|
|
1487
|
+
const inserted = [];
|
|
1488
|
+
let valuesIterator;
|
|
1489
|
+
if (values) {
|
|
1490
|
+
valuesIterator = values[Symbol.iterator]();
|
|
1491
|
+
}
|
|
1492
|
+
for (let keyNodeEntryOrRaw of keysNodesEntriesOrRaws) {
|
|
1493
|
+
let value = void 0;
|
|
1494
|
+
if (valuesIterator) {
|
|
1495
|
+
const valueResult = valuesIterator.next();
|
|
1496
|
+
if (!valueResult.done) {
|
|
1497
|
+
value = valueResult.value;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
if (this.isRaw(keyNodeEntryOrRaw)) keyNodeEntryOrRaw = this._toEntryFn(keyNodeEntryOrRaw);
|
|
1501
|
+
inserted.push(this.add(keyNodeEntryOrRaw, value));
|
|
1502
|
+
}
|
|
1503
|
+
return inserted;
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Merges another tree into this one by adding all its nodes.
|
|
1507
|
+
* @remarks Time O(N * M), same as `addMany`, where N is the size of `anotherTree` and M is the size of this tree. Space O(M) (from `add`).
|
|
1508
|
+
*
|
|
1509
|
+
* @param anotherTree - The tree to merge.
|
|
1510
|
+
*/
|
|
1511
|
+
merge(anotherTree) {
|
|
1512
|
+
this.addMany(anotherTree, []);
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Clears the tree and refills it with new items.
|
|
1516
|
+
* @remarks Time O(N) (for `clear`) + O(N * M) (for `addMany`) = O(N * M). Space O(M) (from `addMany`).
|
|
1517
|
+
*
|
|
1518
|
+
* @param keysNodesEntriesOrRaws - An iterable of items to add.
|
|
1519
|
+
* @param [values] - An optional parallel iterable of values.
|
|
1520
|
+
*/
|
|
1521
|
+
refill(keysNodesEntriesOrRaws, values) {
|
|
1522
|
+
this.clear();
|
|
1523
|
+
this.addMany(keysNodesEntriesOrRaws, values);
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* Deletes a node from the tree.
|
|
1527
|
+
* @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). This implementation finds the node, and if it has two children, swaps it with the rightmost node of its left subtree (in-order predecessor) before deleting. Time O(N) in the worst case. O(N) to find the node (`getNode`) and O(H) (which is O(N) worst-case) to find the rightmost node. Space O(1) (if `getNode` is iterative, which it is).
|
|
1528
|
+
*
|
|
1529
|
+
* @param keyNodeOrEntry - The node to delete.
|
|
1530
|
+
* @returns An array containing deletion results (for compatibility with self-balancing trees).
|
|
1531
|
+
*/
|
|
1532
|
+
delete(keyNodeOrEntry) {
|
|
1533
|
+
const deletedResult = [];
|
|
1534
|
+
if (!this._root) return deletedResult;
|
|
1535
|
+
const curr = this.getNode(keyNodeOrEntry);
|
|
1536
|
+
if (!curr) return deletedResult;
|
|
1537
|
+
const parent = curr == null ? void 0 : curr.parent;
|
|
1538
|
+
let needBalanced;
|
|
1539
|
+
let orgCurrent = curr;
|
|
1540
|
+
if (!curr.left && !curr.right && !parent) {
|
|
1541
|
+
this._setRoot(void 0);
|
|
1542
|
+
} else if (curr.left) {
|
|
1543
|
+
const leftSubTreeRightMost = this.getRightMost((node) => node, curr.left);
|
|
1544
|
+
if (leftSubTreeRightMost) {
|
|
1545
|
+
const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent;
|
|
1546
|
+
orgCurrent = this._swapProperties(curr, leftSubTreeRightMost);
|
|
1547
|
+
if (parentOfLeftSubTreeMax) {
|
|
1548
|
+
if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost)
|
|
1549
|
+
parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left;
|
|
1550
|
+
else parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left;
|
|
1551
|
+
needBalanced = parentOfLeftSubTreeMax;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
} else if (parent) {
|
|
1555
|
+
const { familyPosition: fp } = curr;
|
|
1556
|
+
if (fp === "LEFT" || fp === "ROOT_LEFT") {
|
|
1557
|
+
parent.left = curr.right;
|
|
1558
|
+
} else if (fp === "RIGHT" || fp === "ROOT_RIGHT") {
|
|
1559
|
+
parent.right = curr.right;
|
|
1560
|
+
}
|
|
1561
|
+
needBalanced = parent;
|
|
1562
|
+
} else {
|
|
1563
|
+
this._setRoot(curr.right);
|
|
1564
|
+
curr.right = void 0;
|
|
1565
|
+
}
|
|
1566
|
+
this._size = this._size - 1;
|
|
1567
|
+
deletedResult.push({ deleted: orgCurrent, needBalanced });
|
|
1568
|
+
if (this._isMapMode && orgCurrent) this._store.delete(orgCurrent.key);
|
|
1569
|
+
return deletedResult;
|
|
1570
|
+
}
|
|
1571
|
+
/**
|
|
1572
|
+
* Searches the tree for nodes matching a predicate.
|
|
1573
|
+
* @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). Performs a full DFS (pre-order) scan of the tree. Time O(N), as it may visit every node. Space O(H) for the call stack (recursive) or explicit stack (iterative), where H is the tree height (O(N) worst-case).
|
|
1574
|
+
*
|
|
1575
|
+
* @template C - The type of the callback function.
|
|
1576
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
1577
|
+
* @param [onlyOne=false] - If true, stops after finding the first match.
|
|
1578
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on matching nodes.
|
|
1579
|
+
* @param [startNode=this._root] - The node to start the search from.
|
|
1580
|
+
* @param [iterationType=this.iterationType] - Whether to use 'RECURSIVE' or 'ITERATIVE' search.
|
|
1581
|
+
* @returns An array of results from the callback function for each matching node.
|
|
1582
|
+
*/
|
|
1583
|
+
search(keyNodeEntryOrPredicate, onlyOne = false, callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
1584
|
+
if (keyNodeEntryOrPredicate === void 0) return [];
|
|
1585
|
+
if (keyNodeEntryOrPredicate === null) return [];
|
|
1586
|
+
startNode = this.ensureNode(startNode);
|
|
1587
|
+
if (!startNode) return [];
|
|
1588
|
+
const predicate = this._ensurePredicate(keyNodeEntryOrPredicate);
|
|
1589
|
+
const ans = [];
|
|
1590
|
+
if (iterationType === "RECURSIVE") {
|
|
1591
|
+
const dfs = (cur) => {
|
|
1592
|
+
if (predicate(cur)) {
|
|
1593
|
+
ans.push(callback(cur));
|
|
1594
|
+
if (onlyOne) return;
|
|
1595
|
+
}
|
|
1596
|
+
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
|
|
1597
|
+
if (this.isRealNode(cur.left)) dfs(cur.left);
|
|
1598
|
+
if (this.isRealNode(cur.right)) dfs(cur.right);
|
|
1599
|
+
};
|
|
1600
|
+
dfs(startNode);
|
|
1601
|
+
} else {
|
|
1602
|
+
const stack = [startNode];
|
|
1603
|
+
while (stack.length > 0) {
|
|
1604
|
+
const cur = stack.pop();
|
|
1605
|
+
if (this.isRealNode(cur)) {
|
|
1606
|
+
if (predicate(cur)) {
|
|
1607
|
+
ans.push(callback(cur));
|
|
1608
|
+
if (onlyOne) return ans;
|
|
1609
|
+
}
|
|
1610
|
+
if (this.isRealNode(cur.left)) stack.push(cur.left);
|
|
1611
|
+
if (this.isRealNode(cur.right)) stack.push(cur.right);
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
return ans;
|
|
1616
|
+
}
|
|
1617
|
+
getNodes(keyNodeEntryOrPredicate, onlyOne = false, startNode = this._root, iterationType = this.iterationType) {
|
|
1618
|
+
return this.search(keyNodeEntryOrPredicate, onlyOne, (node) => node, startNode, iterationType);
|
|
1619
|
+
}
|
|
1620
|
+
/**
|
|
1621
|
+
* Gets the first node matching a predicate.
|
|
1622
|
+
* @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). Time O(N) in the worst case (via `search`). Space O(H) or O(N) (via `search`).
|
|
1623
|
+
*
|
|
1624
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
1625
|
+
* @param [startNode=this._root] - The node to start the search from.
|
|
1626
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
1627
|
+
* @returns The first matching node, or undefined if not found.
|
|
1628
|
+
*/
|
|
1629
|
+
getNode(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
|
|
1630
|
+
return this.search(keyNodeEntryOrPredicate, true, (node) => node, startNode, iterationType)[0];
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Gets the value associated with a key.
|
|
1634
|
+
* @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). Time O(1) if in Map mode. O(N) if not in Map mode (uses `getNode`). Space O(1) if in Map mode. O(H) or O(N) otherwise.
|
|
1635
|
+
*
|
|
1636
|
+
* @param keyNodeEntryOrPredicate - The key, node, or entry to get the value for.
|
|
1637
|
+
* @param [startNode=this._root] - The node to start searching from (if not in Map mode).
|
|
1638
|
+
* @param [iterationType=this.iterationType] - The traversal method (if not in Map mode).
|
|
1639
|
+
* @returns The associated value, or undefined.
|
|
1640
|
+
*/
|
|
1641
|
+
get(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
|
|
1642
|
+
var _a;
|
|
1643
|
+
if (this._isMapMode) {
|
|
1644
|
+
const key = this._extractKey(keyNodeEntryOrPredicate);
|
|
1645
|
+
if (key === null || key === void 0) return;
|
|
1646
|
+
return this._store.get(key);
|
|
1647
|
+
}
|
|
1648
|
+
return (_a = this.getNode(keyNodeEntryOrPredicate, startNode, iterationType)) == null ? void 0 : _a.value;
|
|
1649
|
+
}
|
|
1650
|
+
has(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
|
|
1651
|
+
return this.search(keyNodeEntryOrPredicate, true, (node) => node, startNode, iterationType).length > 0;
|
|
1652
|
+
}
|
|
1653
|
+
/**
|
|
1654
|
+
* Clears the tree of all nodes and values.
|
|
1655
|
+
* @remarks Time O(N) if in Map mode (due to `_store.clear()`), O(1) otherwise. Space O(1)
|
|
1656
|
+
*/
|
|
1657
|
+
clear() {
|
|
1658
|
+
this._clearNodes();
|
|
1659
|
+
if (this._isMapMode) this._clearValues();
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Checks if the tree is empty.
|
|
1663
|
+
* @remarks Time O(1), Space O(1)
|
|
1664
|
+
*
|
|
1665
|
+
* @returns True if the tree has no nodes, false otherwise.
|
|
1666
|
+
*/
|
|
1667
|
+
isEmpty() {
|
|
1668
|
+
return this._size === 0;
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Checks if the tree is perfectly balanced.
|
|
1672
|
+
* @remarks A tree is perfectly balanced if the difference between min and max height is at most 1. Time O(N), as it requires two full traversals (`getMinHeight` and `getHeight`). Space O(H) or O(N) (from height calculation).
|
|
1673
|
+
*
|
|
1674
|
+
* @param [startNode=this._root] - The node to start checking from.
|
|
1675
|
+
* @returns True if perfectly balanced, false otherwise.
|
|
1676
|
+
*/
|
|
1677
|
+
isPerfectlyBalanced(startNode = this._root) {
|
|
1678
|
+
return this.getMinHeight(startNode) + 1 >= this.getHeight(startNode);
|
|
1679
|
+
}
|
|
1680
|
+
/**
|
|
1681
|
+
* Checks if the tree is a valid Binary Search Tree (BST).
|
|
1682
|
+
* @remarks Time O(N), as it must visit every node. Space O(H) for the call stack (recursive) or explicit stack (iterative), where H is the tree height (O(N) worst-case).
|
|
1683
|
+
*
|
|
1684
|
+
* @param [startNode=this._root] - The node to start checking from.
|
|
1685
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
1686
|
+
* @returns True if it's a valid BST, false otherwise.
|
|
1687
|
+
*/
|
|
1688
|
+
isBST(startNode = this._root, iterationType = this.iterationType) {
|
|
1689
|
+
const startNodeSired = this.ensureNode(startNode);
|
|
1690
|
+
if (!startNodeSired) return true;
|
|
1691
|
+
if (iterationType === "RECURSIVE") {
|
|
1692
|
+
const dfs = (cur, min, max) => {
|
|
1693
|
+
if (!this.isRealNode(cur)) return true;
|
|
1694
|
+
const numKey = Number(cur.key);
|
|
1695
|
+
if (numKey <= min || numKey >= max) return false;
|
|
1696
|
+
return dfs(cur.left, min, numKey) && dfs(cur.right, numKey, max);
|
|
1697
|
+
};
|
|
1698
|
+
const isStandardBST = dfs(startNodeSired, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
|
|
1699
|
+
const isInverseBST = dfs(startNodeSired, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER);
|
|
1700
|
+
return isStandardBST || isInverseBST;
|
|
1701
|
+
} else {
|
|
1702
|
+
const checkBST = (checkMax = false) => {
|
|
1703
|
+
const stack = [];
|
|
1704
|
+
let prev = checkMax ? Number.MAX_SAFE_INTEGER : Number.MIN_SAFE_INTEGER;
|
|
1705
|
+
let curr = startNodeSired;
|
|
1706
|
+
while (this.isRealNode(curr) || stack.length > 0) {
|
|
1707
|
+
while (this.isRealNode(curr)) {
|
|
1708
|
+
stack.push(curr);
|
|
1709
|
+
curr = curr.left;
|
|
1710
|
+
}
|
|
1711
|
+
curr = stack.pop();
|
|
1712
|
+
const numKey = Number(curr.key);
|
|
1713
|
+
if (!this.isRealNode(curr) || !checkMax && prev >= numKey || checkMax && prev <= numKey) return false;
|
|
1714
|
+
prev = numKey;
|
|
1715
|
+
curr = curr.right;
|
|
1716
|
+
}
|
|
1717
|
+
return true;
|
|
1718
|
+
};
|
|
1719
|
+
const isStandardBST = checkBST(false);
|
|
1720
|
+
const isInverseBST = checkBST(true);
|
|
1721
|
+
return isStandardBST || isInverseBST;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Gets the depth of a node (distance from `startNode`).
|
|
1726
|
+
* @remarks Time O(H), where H is the depth of the `dist` node relative to `startNode`. O(N) worst-case. Space O(1).
|
|
1727
|
+
*
|
|
1728
|
+
* @param dist - The node to find the depth of.
|
|
1729
|
+
* @param [startNode=this._root] - The node to measure depth from (defaults to root).
|
|
1730
|
+
* @returns The depth (0 if `dist` is `startNode`).
|
|
1731
|
+
*/
|
|
1732
|
+
getDepth(dist, startNode = this._root) {
|
|
1733
|
+
let distEnsured = this.ensureNode(dist);
|
|
1734
|
+
const beginRootEnsured = this.ensureNode(startNode);
|
|
1735
|
+
let depth = 0;
|
|
1736
|
+
while (distEnsured == null ? void 0 : distEnsured.parent) {
|
|
1737
|
+
if (distEnsured === beginRootEnsured) {
|
|
1738
|
+
return depth;
|
|
1739
|
+
}
|
|
1740
|
+
depth++;
|
|
1741
|
+
distEnsured = distEnsured.parent;
|
|
1742
|
+
}
|
|
1743
|
+
return depth;
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Gets the maximum height of the tree (longest path from startNode to a leaf).
|
|
1747
|
+
* @remarks Time O(N), as it must visit every node. Space O(H) for recursive stack (O(N) worst-case) or O(N) for iterative stack (storing node + depth).
|
|
1748
|
+
*
|
|
1749
|
+
* @param [startNode=this._root] - The node to start measuring from.
|
|
1750
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
1751
|
+
* @returns The height ( -1 for an empty tree, 0 for a single-node tree).
|
|
1752
|
+
*/
|
|
1753
|
+
getHeight(startNode = this._root, iterationType = this.iterationType) {
|
|
1754
|
+
startNode = this.ensureNode(startNode);
|
|
1755
|
+
if (!this.isRealNode(startNode)) return -1;
|
|
1756
|
+
if (iterationType === "RECURSIVE") {
|
|
1757
|
+
const _getMaxHeight = (cur) => {
|
|
1758
|
+
if (!this.isRealNode(cur)) return -1;
|
|
1759
|
+
const leftHeight = _getMaxHeight(cur.left);
|
|
1760
|
+
const rightHeight = _getMaxHeight(cur.right);
|
|
1761
|
+
return Math.max(leftHeight, rightHeight) + 1;
|
|
1762
|
+
};
|
|
1763
|
+
return _getMaxHeight(startNode);
|
|
1764
|
+
} else {
|
|
1765
|
+
const stack = [{ node: startNode, depth: 0 }];
|
|
1766
|
+
let maxHeight = 0;
|
|
1767
|
+
while (stack.length > 0) {
|
|
1768
|
+
const { node, depth } = stack.pop();
|
|
1769
|
+
if (this.isRealNode(node.left)) stack.push({ node: node.left, depth: depth + 1 });
|
|
1770
|
+
if (this.isRealNode(node.right)) stack.push({ node: node.right, depth: depth + 1 });
|
|
1771
|
+
maxHeight = Math.max(maxHeight, depth);
|
|
1772
|
+
}
|
|
1773
|
+
return maxHeight;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Gets the minimum height of the tree (shortest path from startNode to a leaf).
|
|
1778
|
+
* @remarks Time O(N), as it must visit every node. Space O(H) for recursive stack (O(N) worst-case) or O(N) for iterative (due to `depths` Map).
|
|
1779
|
+
*
|
|
1780
|
+
* @param [startNode=this._root] - The node to start measuring from.
|
|
1781
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
1782
|
+
* @returns The minimum height (-1 for empty, 0 for single node).
|
|
1783
|
+
*/
|
|
1784
|
+
getMinHeight(startNode = this._root, iterationType = this.iterationType) {
|
|
1785
|
+
startNode = this.ensureNode(startNode);
|
|
1786
|
+
if (!startNode) return -1;
|
|
1787
|
+
if (iterationType === "RECURSIVE") {
|
|
1788
|
+
const _getMinHeight = (cur) => {
|
|
1789
|
+
if (!this.isRealNode(cur)) return 0;
|
|
1790
|
+
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return 0;
|
|
1791
|
+
const leftMinHeight = _getMinHeight(cur.left);
|
|
1792
|
+
const rightMinHeight = _getMinHeight(cur.right);
|
|
1793
|
+
return Math.min(leftMinHeight, rightMinHeight) + 1;
|
|
1794
|
+
};
|
|
1795
|
+
return _getMinHeight(startNode);
|
|
1796
|
+
} else {
|
|
1797
|
+
const stack = [];
|
|
1798
|
+
let node = startNode, last = null;
|
|
1799
|
+
const depths = /* @__PURE__ */ new Map();
|
|
1800
|
+
while (stack.length > 0 || node) {
|
|
1801
|
+
if (this.isRealNode(node)) {
|
|
1802
|
+
stack.push(node);
|
|
1803
|
+
node = node.left;
|
|
1804
|
+
} else {
|
|
1805
|
+
node = stack[stack.length - 1];
|
|
1806
|
+
if (!this.isRealNode(node.right) || last === node.right) {
|
|
1807
|
+
node = stack.pop();
|
|
1808
|
+
if (this.isRealNode(node)) {
|
|
1809
|
+
const leftMinHeight = this.isRealNode(node.left) ? depths.get(node.left) : -1;
|
|
1810
|
+
const rightMinHeight = this.isRealNode(node.right) ? depths.get(node.right) : -1;
|
|
1811
|
+
depths.set(node, 1 + Math.min(leftMinHeight, rightMinHeight));
|
|
1812
|
+
last = node;
|
|
1813
|
+
node = null;
|
|
1814
|
+
}
|
|
1815
|
+
} else node = node.right;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
return depths.get(startNode);
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* Gets the path from a given node up to the root.
|
|
1823
|
+
* @remarks Time O(H), where H is the depth of the `beginNode`. O(N) worst-case. Space O(H) for the result array.
|
|
1824
|
+
*
|
|
1825
|
+
* @template C - The type of the callback function.
|
|
1826
|
+
* @param beginNode - The node to start the path from.
|
|
1827
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on each node in the path.
|
|
1828
|
+
* @param [isReverse=false] - If true, returns the path from root-to-node.
|
|
1829
|
+
* @returns An array of callback results.
|
|
1830
|
+
*/
|
|
1831
|
+
getPathToRoot(beginNode, callback = this._DEFAULT_NODE_CALLBACK, isReverse = false) {
|
|
1832
|
+
const result = [];
|
|
1833
|
+
let beginNodeEnsured = this.ensureNode(beginNode);
|
|
1834
|
+
if (!beginNodeEnsured) return result;
|
|
1835
|
+
while (beginNodeEnsured.parent) {
|
|
1836
|
+
result.push(callback(beginNodeEnsured));
|
|
1837
|
+
beginNodeEnsured = beginNodeEnsured.parent;
|
|
1838
|
+
}
|
|
1839
|
+
result.push(callback(beginNodeEnsured));
|
|
1840
|
+
return isReverse ? result.reverse() : result;
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1843
|
+
* Finds the leftmost node in a subtree (the node with the smallest key in a BST).
|
|
1844
|
+
* @remarks Time O(H), where H is the height of the left spine. O(N) worst-case. Space O(H) for recursive/trampoline stack.
|
|
1845
|
+
*
|
|
1846
|
+
* @template C - The type of the callback function.
|
|
1847
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on the leftmost node.
|
|
1848
|
+
* @param [startNode=this._root] - The subtree root to search from.
|
|
1849
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
1850
|
+
* @returns The callback result for the leftmost node.
|
|
1851
|
+
*/
|
|
1852
|
+
getLeftMost(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
1853
|
+
if (this.isNIL(startNode)) return callback(void 0);
|
|
1854
|
+
const ensuredStartNode = this.ensureNode(startNode);
|
|
1855
|
+
if (!this.isRealNode(ensuredStartNode)) return callback(void 0);
|
|
1856
|
+
if (iterationType === "RECURSIVE") {
|
|
1857
|
+
const dfs = (cur) => {
|
|
1858
|
+
const { left } = cur;
|
|
1859
|
+
if (!this.isRealNode(left)) return cur;
|
|
1860
|
+
return dfs(left);
|
|
1861
|
+
};
|
|
1862
|
+
return callback(dfs(ensuredStartNode));
|
|
1863
|
+
} else {
|
|
1864
|
+
const dfs = makeTrampoline((cur) => {
|
|
1865
|
+
const { left } = cur;
|
|
1866
|
+
if (!this.isRealNode(left)) return cur;
|
|
1867
|
+
return makeTrampolineThunk(() => dfs(left));
|
|
1868
|
+
});
|
|
1869
|
+
return callback(dfs(ensuredStartNode));
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* Finds the rightmost node in a subtree (the node with the largest key in a BST).
|
|
1874
|
+
* @remarks Time O(H), where H is the height of the right spine. O(N) worst-case. Space O(H) for recursive/trampoline stack.
|
|
1875
|
+
*
|
|
1876
|
+
* @template C - The type of the callback function.
|
|
1877
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on the rightmost node.
|
|
1878
|
+
* @param [startNode=this._root] - The subtree root to search from.
|
|
1879
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
1880
|
+
* @returns The callback result for the rightmost node.
|
|
1881
|
+
*/
|
|
1882
|
+
getRightMost(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
1883
|
+
if (this.isNIL(startNode)) return callback(void 0);
|
|
1884
|
+
startNode = this.ensureNode(startNode);
|
|
1885
|
+
if (!startNode) return callback(void 0);
|
|
1886
|
+
if (iterationType === "RECURSIVE") {
|
|
1887
|
+
const dfs = (cur) => {
|
|
1888
|
+
const { right } = cur;
|
|
1889
|
+
if (!this.isRealNode(right)) return cur;
|
|
1890
|
+
return dfs(right);
|
|
1891
|
+
};
|
|
1892
|
+
return callback(dfs(startNode));
|
|
1893
|
+
} else {
|
|
1894
|
+
const dfs = makeTrampoline((cur) => {
|
|
1895
|
+
const { right } = cur;
|
|
1896
|
+
if (!this.isRealNode(right)) return cur;
|
|
1897
|
+
return makeTrampolineThunk(() => dfs(right));
|
|
1898
|
+
});
|
|
1899
|
+
return callback(dfs(startNode));
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Gets the Morris traversal predecessor (rightmost node in the left subtree, or node itself).
|
|
1904
|
+
* @remarks This is primarily a helper for Morris traversal. Time O(H), where H is the height of the left subtree. O(N) worst-case. Space O(1).
|
|
1905
|
+
*
|
|
1906
|
+
* @param node - The node to find the predecessor for.
|
|
1907
|
+
* @returns The Morris predecessor.
|
|
1908
|
+
*/
|
|
1909
|
+
getPredecessor(node) {
|
|
1910
|
+
if (this.isRealNode(node.left)) {
|
|
1911
|
+
let predecessor = node.left;
|
|
1912
|
+
while (!this.isRealNode(predecessor) || this.isRealNode(predecessor.right) && predecessor.right !== node) {
|
|
1913
|
+
if (this.isRealNode(predecessor)) {
|
|
1914
|
+
predecessor = predecessor.right;
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
return predecessor;
|
|
1918
|
+
} else {
|
|
1919
|
+
return node;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
/**
|
|
1923
|
+
* Gets the in-order successor of a node in a BST.
|
|
1924
|
+
* @remarks Time O(H), where H is the tree height. O(N) worst-case. Space O(H) (due to `getLeftMost` stack).
|
|
1925
|
+
*
|
|
1926
|
+
* @param [x] - The node to find the successor of.
|
|
1927
|
+
* @returns The successor node, or null/undefined if none exists.
|
|
1928
|
+
*/
|
|
1929
|
+
getSuccessor(x) {
|
|
1930
|
+
x = this.ensureNode(x);
|
|
1931
|
+
if (!this.isRealNode(x)) return void 0;
|
|
1932
|
+
if (this.isRealNode(x.right)) {
|
|
1933
|
+
return this.getLeftMost((node) => node, x.right);
|
|
1934
|
+
}
|
|
1935
|
+
let y = x.parent;
|
|
1936
|
+
while (this.isRealNode(y) && x === y.right) {
|
|
1937
|
+
x = y;
|
|
1938
|
+
y = y.parent;
|
|
1939
|
+
}
|
|
1940
|
+
return y;
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Performs a Depth-First Search (DFS) traversal.
|
|
1944
|
+
* @remarks Time O(N), visits every node. Space O(H) for the call/explicit stack. O(N) worst-case.
|
|
1945
|
+
*
|
|
1946
|
+
* @template C - The type of the callback function.
|
|
1947
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
|
|
1948
|
+
* @param [pattern='IN'] - The traversal order ('IN', 'PRE', 'POST').
|
|
1949
|
+
* @param [onlyOne=false] - If true, stops after the first callback.
|
|
1950
|
+
* @param [startNode=this._root] - The node to start from.
|
|
1951
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
1952
|
+
* @param [includeNull=false] - If true, includes null nodes in the traversal.
|
|
1953
|
+
* @returns An array of callback results.
|
|
1954
|
+
*/
|
|
1955
|
+
dfs(callback = this._DEFAULT_NODE_CALLBACK, pattern = "IN", onlyOne = false, startNode = this._root, iterationType = this.iterationType, includeNull = false) {
|
|
1956
|
+
startNode = this.ensureNode(startNode);
|
|
1957
|
+
if (!startNode) return [];
|
|
1958
|
+
return this._dfs(callback, pattern, onlyOne, startNode, iterationType, includeNull);
|
|
1959
|
+
}
|
|
1960
|
+
/**
|
|
1961
|
+
* Performs a Breadth-First Search (BFS) or Level-Order traversal.
|
|
1962
|
+
* @remarks Time O(N), visits every node. Space O(N) in the worst case for the queue (e.g., a full last level).
|
|
1963
|
+
*
|
|
1964
|
+
* @template C - The type of the callback function.
|
|
1965
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
|
|
1966
|
+
* @param [startNode=this._root] - The node to start from.
|
|
1967
|
+
* @param [iterationType=this.iterationType] - The traversal method ('RECURSIVE' BFS is less common but supported here).
|
|
1968
|
+
* @param [includeNull=false] - If true, includes null nodes in the traversal.
|
|
1969
|
+
* @returns An array of callback results.
|
|
1970
|
+
*/
|
|
1971
|
+
bfs(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType, includeNull = false) {
|
|
1972
|
+
startNode = this.ensureNode(startNode);
|
|
1973
|
+
if (!startNode) return [];
|
|
1974
|
+
const ans = [];
|
|
1975
|
+
if (iterationType === "RECURSIVE") {
|
|
1976
|
+
const queue = new Queue([
|
|
1977
|
+
startNode
|
|
1978
|
+
]);
|
|
1979
|
+
const dfs = (level) => {
|
|
1980
|
+
if (queue.length === 0) return;
|
|
1981
|
+
const current = queue.shift();
|
|
1982
|
+
ans.push(callback(current));
|
|
1983
|
+
if (includeNull) {
|
|
1984
|
+
if (current && this.isRealNodeOrNull(current.left)) queue.push(current.left);
|
|
1985
|
+
if (current && this.isRealNodeOrNull(current.right)) queue.push(current.right);
|
|
1986
|
+
} else {
|
|
1987
|
+
if (this.isRealNode(current.left)) queue.push(current.left);
|
|
1988
|
+
if (this.isRealNode(current.right)) queue.push(current.right);
|
|
1989
|
+
}
|
|
1990
|
+
dfs(level + 1);
|
|
1991
|
+
};
|
|
1992
|
+
dfs(0);
|
|
1993
|
+
} else {
|
|
1994
|
+
const queue = new Queue([startNode]);
|
|
1995
|
+
while (queue.length > 0) {
|
|
1996
|
+
const levelSize = queue.length;
|
|
1997
|
+
for (let i = 0; i < levelSize; i++) {
|
|
1998
|
+
const current = queue.shift();
|
|
1999
|
+
ans.push(callback(current));
|
|
2000
|
+
if (includeNull) {
|
|
2001
|
+
if (current && this.isRealNodeOrNull(current.left)) queue.push(current.left);
|
|
2002
|
+
if (current && this.isRealNodeOrNull(current.right)) queue.push(current.right);
|
|
2003
|
+
} else {
|
|
2004
|
+
if (this.isRealNode(current.left)) queue.push(current.left);
|
|
2005
|
+
if (this.isRealNode(current.right)) queue.push(current.right);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
return ans;
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* Finds all leaf nodes in the tree.
|
|
2014
|
+
* @remarks Time O(N), visits every node. Space O(H) for recursive stack or O(N) for iterative queue.
|
|
2015
|
+
*
|
|
2016
|
+
* @template C - The type of the callback function.
|
|
2017
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each leaf node.
|
|
2018
|
+
* @param [startNode=this._root] - The node to start from.
|
|
2019
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
2020
|
+
* @returns An array of callback results.
|
|
2021
|
+
*/
|
|
2022
|
+
leaves(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
2023
|
+
startNode = this.ensureNode(startNode);
|
|
2024
|
+
const leaves = [];
|
|
2025
|
+
if (!this.isRealNode(startNode)) return [];
|
|
2026
|
+
if (iterationType === "RECURSIVE") {
|
|
2027
|
+
const dfs = (cur) => {
|
|
2028
|
+
if (this.isLeaf(cur)) {
|
|
2029
|
+
leaves.push(callback(cur));
|
|
2030
|
+
}
|
|
2031
|
+
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
|
|
2032
|
+
if (this.isRealNode(cur.left)) dfs(cur.left);
|
|
2033
|
+
if (this.isRealNode(cur.right)) dfs(cur.right);
|
|
2034
|
+
};
|
|
2035
|
+
dfs(startNode);
|
|
2036
|
+
} else {
|
|
2037
|
+
const queue = new Queue([startNode]);
|
|
2038
|
+
while (queue.length > 0) {
|
|
2039
|
+
const cur = queue.shift();
|
|
2040
|
+
if (this.isRealNode(cur)) {
|
|
2041
|
+
if (this.isLeaf(cur)) {
|
|
2042
|
+
leaves.push(callback(cur));
|
|
2043
|
+
}
|
|
2044
|
+
if (this.isRealNode(cur.left)) queue.push(cur.left);
|
|
2045
|
+
if (this.isRealNode(cur.right)) queue.push(cur.right);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
return leaves;
|
|
2050
|
+
}
|
|
2051
|
+
/**
|
|
2052
|
+
* Returns a 2D array of nodes, grouped by level.
|
|
2053
|
+
* @remarks Time O(N), visits every node. Space O(N) for the result array and the queue/stack.
|
|
2054
|
+
*
|
|
2055
|
+
* @template C - The type of the callback function.
|
|
2056
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
|
|
2057
|
+
* @param [startNode=this._root] - The node to start from.
|
|
2058
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
2059
|
+
* @param [includeNull=false] - If true, includes null nodes.
|
|
2060
|
+
* @returns A 2D array of callback results.
|
|
2061
|
+
*/
|
|
2062
|
+
listLevels(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType, includeNull = false) {
|
|
2063
|
+
startNode = this.ensureNode(startNode);
|
|
2064
|
+
const levelsNodes = [];
|
|
2065
|
+
if (!startNode) return levelsNodes;
|
|
2066
|
+
if (iterationType === "RECURSIVE") {
|
|
2067
|
+
const _recursive = (node, level) => {
|
|
2068
|
+
if (!levelsNodes[level]) levelsNodes[level] = [];
|
|
2069
|
+
levelsNodes[level].push(callback(node));
|
|
2070
|
+
if (includeNull) {
|
|
2071
|
+
if (node && this.isRealNodeOrNull(node.left)) _recursive(node.left, level + 1);
|
|
2072
|
+
if (node && this.isRealNodeOrNull(node.right)) _recursive(node.right, level + 1);
|
|
2073
|
+
} else {
|
|
2074
|
+
if (node && node.left) _recursive(node.left, level + 1);
|
|
2075
|
+
if (node && node.right) _recursive(node.right, level + 1);
|
|
2076
|
+
}
|
|
2077
|
+
};
|
|
2078
|
+
_recursive(startNode, 0);
|
|
2079
|
+
} else {
|
|
2080
|
+
const stack = [[startNode, 0]];
|
|
2081
|
+
while (stack.length > 0) {
|
|
2082
|
+
const head = stack.pop();
|
|
2083
|
+
const [node, level] = head;
|
|
2084
|
+
if (!levelsNodes[level]) levelsNodes[level] = [];
|
|
2085
|
+
levelsNodes[level].push(callback(node));
|
|
2086
|
+
if (includeNull) {
|
|
2087
|
+
if (node && this.isRealNodeOrNull(node.right)) stack.push([node.right, level + 1]);
|
|
2088
|
+
if (node && this.isRealNodeOrNull(node.left)) stack.push([node.left, level + 1]);
|
|
2089
|
+
} else {
|
|
2090
|
+
if (node && node.right) stack.push([node.right, level + 1]);
|
|
2091
|
+
if (node && node.left) stack.push([node.left, level + 1]);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
return levelsNodes;
|
|
2096
|
+
}
|
|
2097
|
+
/**
|
|
2098
|
+
* Performs a Morris (threaded) traversal.
|
|
2099
|
+
* @remarks This traversal uses O(1) extra space (excluding the result array) by temporarily modifying the tree's right child pointers. Time O(N), as each node is visited a constant number of times. Space O(1) (excluding the `ans` array).
|
|
2100
|
+
*
|
|
2101
|
+
* @template C - The type of the callback function.
|
|
2102
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
|
|
2103
|
+
* @param [pattern='IN'] - The traversal order ('IN', 'PRE', 'POST').
|
|
2104
|
+
* @param [startNode=this._root] - The node to start from.
|
|
2105
|
+
* @returns An array of callback results.
|
|
2106
|
+
*/
|
|
2107
|
+
morris(callback = this._DEFAULT_NODE_CALLBACK, pattern = "IN", startNode = this._root) {
|
|
2108
|
+
startNode = this.ensureNode(startNode);
|
|
2109
|
+
if (!startNode) return [];
|
|
2110
|
+
const ans = [];
|
|
2111
|
+
let cur = startNode;
|
|
2112
|
+
const _reverseEdge = (node) => {
|
|
2113
|
+
let pre = null;
|
|
2114
|
+
let next = null;
|
|
2115
|
+
while (node) {
|
|
2116
|
+
next = node.right;
|
|
2117
|
+
node.right = pre;
|
|
2118
|
+
pre = node;
|
|
2119
|
+
node = next;
|
|
2120
|
+
}
|
|
2121
|
+
return pre;
|
|
2122
|
+
};
|
|
2123
|
+
const _printEdge = (node) => {
|
|
2124
|
+
const tail = _reverseEdge(node);
|
|
2125
|
+
let cur2 = tail;
|
|
2126
|
+
while (cur2) {
|
|
2127
|
+
ans.push(callback(cur2));
|
|
2128
|
+
cur2 = cur2.right;
|
|
2129
|
+
}
|
|
2130
|
+
_reverseEdge(tail);
|
|
2131
|
+
};
|
|
2132
|
+
switch (pattern) {
|
|
2133
|
+
case "IN":
|
|
2134
|
+
while (cur) {
|
|
2135
|
+
if (cur.left) {
|
|
2136
|
+
const predecessor = this.getPredecessor(cur);
|
|
2137
|
+
if (!predecessor.right) {
|
|
2138
|
+
predecessor.right = cur;
|
|
2139
|
+
cur = cur.left;
|
|
2140
|
+
continue;
|
|
2141
|
+
} else {
|
|
2142
|
+
predecessor.right = null;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
ans.push(callback(cur));
|
|
2146
|
+
cur = cur.right;
|
|
2147
|
+
}
|
|
2148
|
+
break;
|
|
2149
|
+
case "PRE":
|
|
2150
|
+
while (cur) {
|
|
2151
|
+
if (cur.left) {
|
|
2152
|
+
const predecessor = this.getPredecessor(cur);
|
|
2153
|
+
if (!predecessor.right) {
|
|
2154
|
+
predecessor.right = cur;
|
|
2155
|
+
ans.push(callback(cur));
|
|
2156
|
+
cur = cur.left;
|
|
2157
|
+
continue;
|
|
2158
|
+
} else {
|
|
2159
|
+
predecessor.right = null;
|
|
2160
|
+
}
|
|
2161
|
+
} else {
|
|
2162
|
+
ans.push(callback(cur));
|
|
2163
|
+
}
|
|
2164
|
+
cur = cur.right;
|
|
2165
|
+
}
|
|
2166
|
+
break;
|
|
2167
|
+
case "POST":
|
|
2168
|
+
while (cur) {
|
|
2169
|
+
if (cur.left) {
|
|
2170
|
+
const predecessor = this.getPredecessor(cur);
|
|
2171
|
+
if (predecessor.right === null) {
|
|
2172
|
+
predecessor.right = cur;
|
|
2173
|
+
cur = cur.left;
|
|
2174
|
+
continue;
|
|
2175
|
+
} else {
|
|
2176
|
+
predecessor.right = null;
|
|
2177
|
+
_printEdge(cur.left);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
cur = cur.right;
|
|
2181
|
+
}
|
|
2182
|
+
_printEdge(startNode);
|
|
2183
|
+
break;
|
|
2184
|
+
}
|
|
2185
|
+
return ans;
|
|
2186
|
+
}
|
|
2187
|
+
/**
|
|
2188
|
+
* Clones the tree.
|
|
2189
|
+
* @remarks Time O(N * M), where N is the number of nodes and M is the tree size during insertion (due to `bfs` + `add`, and `add` is O(M)). Space O(N) for the new tree and the BFS queue.
|
|
2190
|
+
*
|
|
2191
|
+
* @returns A new, cloned instance of the tree.
|
|
2192
|
+
*/
|
|
2193
|
+
clone() {
|
|
2194
|
+
const out = this._createInstance();
|
|
2195
|
+
this._clone(out);
|
|
2196
|
+
return out;
|
|
2197
|
+
}
|
|
2198
|
+
/**
|
|
2199
|
+
* Creates a new tree containing only the entries that satisfy the predicate.
|
|
2200
|
+
* @remarks Time O(N * M), where N is nodes in this tree, and M is size of the new tree during insertion (O(N) iteration + O(M) `add` for each item). Space O(N) for the new tree.
|
|
2201
|
+
*
|
|
2202
|
+
* @param predicate - A function to test each [key, value] pair.
|
|
2203
|
+
* @param [thisArg] - `this` context for the predicate.
|
|
2204
|
+
* @returns A new, filtered tree.
|
|
2205
|
+
*/
|
|
2206
|
+
filter(predicate, thisArg) {
|
|
2207
|
+
const out = this._createInstance();
|
|
2208
|
+
let i = 0;
|
|
2209
|
+
for (const [k, v] of this) if (predicate.call(thisArg, k, v, i++, this)) out.add([k, v]);
|
|
2210
|
+
return out;
|
|
2211
|
+
}
|
|
2212
|
+
/**
|
|
2213
|
+
* Creates a new tree by mapping each [key, value] pair to a new entry.
|
|
2214
|
+
* @remarks Time O(N * M), where N is nodes in this tree, and M is size of the new tree during insertion. Space O(N) for the new tree.
|
|
2215
|
+
*
|
|
2216
|
+
* @template MK - New key type.
|
|
2217
|
+
* @template MV - New value type.
|
|
2218
|
+
* @template MR - New raw type.
|
|
2219
|
+
* @param cb - A function to map each [key, value] pair.
|
|
2220
|
+
* @param [options] - Options for the new tree.
|
|
2221
|
+
* @param [thisArg] - `this` context for the callback.
|
|
2222
|
+
* @returns A new, mapped tree.
|
|
2223
|
+
*/
|
|
2224
|
+
map(cb, options, thisArg) {
|
|
2225
|
+
const out = this._createLike([], options);
|
|
2226
|
+
let i = 0;
|
|
2227
|
+
for (const [k, v] of this) out.add(cb.call(thisArg, k, v, i++, this));
|
|
2228
|
+
return out;
|
|
2229
|
+
}
|
|
2230
|
+
/**
|
|
2231
|
+
* Generates a string representation of the tree for visualization.
|
|
2232
|
+
* @remarks Time O(N), visits every node. Space O(N*H) or O(N^2) in the worst case, as the string width can grow significantly.
|
|
2233
|
+
*
|
|
2234
|
+
* @param [startNode=this._root] - The node to start printing from.
|
|
2235
|
+
* @param [options] - Options to control the output (e.g., show nulls).
|
|
2236
|
+
* @returns The string representation of the tree.
|
|
2237
|
+
*/
|
|
2238
|
+
toVisual(startNode = this._root, options) {
|
|
2239
|
+
const opts = { isShowUndefined: false, isShowNull: true, isShowRedBlackNIL: false, ...options };
|
|
2240
|
+
startNode = this.ensureNode(startNode);
|
|
2241
|
+
let output = "";
|
|
2242
|
+
if (!startNode) return output;
|
|
2243
|
+
if (opts.isShowUndefined) output += `U for undefined
|
|
2244
|
+
`;
|
|
2245
|
+
if (opts.isShowNull) output += `N for null
|
|
2246
|
+
`;
|
|
2247
|
+
if (opts.isShowRedBlackNIL) output += `S for Sentinel Node(NIL)
|
|
2248
|
+
`;
|
|
2249
|
+
const display = (root) => {
|
|
2250
|
+
const [lines] = this._displayAux(root, opts);
|
|
2251
|
+
let paragraph = "";
|
|
2252
|
+
for (const line of lines) {
|
|
2253
|
+
paragraph += line + "\n";
|
|
2254
|
+
}
|
|
2255
|
+
output += paragraph;
|
|
2256
|
+
};
|
|
2257
|
+
display(startNode);
|
|
2258
|
+
return output;
|
|
2259
|
+
}
|
|
2260
|
+
/**
|
|
2261
|
+
* Prints a visual representation of the tree to the console.
|
|
2262
|
+
* @remarks Time O(N) (via `toVisual`). Space O(N*H) or O(N^2) (via `toVisual`).
|
|
2263
|
+
*
|
|
2264
|
+
* @param [options] - Options to control the output.
|
|
2265
|
+
* @param [startNode=this._root] - The node to start printing from.
|
|
2266
|
+
*/
|
|
2267
|
+
print(options, startNode = this._root) {
|
|
2268
|
+
console.log(this.toVisual(startNode, options));
|
|
2269
|
+
}
|
|
2270
|
+
/**
|
|
2271
|
+
* (Protected) Core DFS implementation.
|
|
2272
|
+
* @remarks Time O(N), visits every node satisfying predicates. Space O(H) for call/explicit stack. O(N) worst-case.
|
|
2273
|
+
*
|
|
2274
|
+
* @template C - Callback type.
|
|
2275
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on nodes.
|
|
2276
|
+
* @param [pattern='IN'] - Traversal order.
|
|
2277
|
+
* @param [onlyOne=false] - Stop after first match.
|
|
2278
|
+
* @param [startNode=this._root] - Starting node.
|
|
2279
|
+
* @param [iterationType=this.iterationType] - Traversal method.
|
|
2280
|
+
* @param [includeNull=false] - Include nulls.
|
|
2281
|
+
* @param [shouldVisitLeft] - Predicate to traverse left.
|
|
2282
|
+
* @param [shouldVisitRight] - Predicate to traverse right.
|
|
2283
|
+
* @param [shouldVisitRoot] - Predicate to visit root.
|
|
2284
|
+
* @param [shouldProcessRoot] - Predicate to process root.
|
|
2285
|
+
* @returns Array of callback results.
|
|
2286
|
+
*/
|
|
2287
|
+
_dfs(callback = this._DEFAULT_NODE_CALLBACK, pattern = "IN", onlyOne = false, startNode = this._root, iterationType = this.iterationType, includeNull = false, shouldVisitLeft = (node) => !!node, shouldVisitRight = (node) => !!node, shouldVisitRoot = (node) => {
|
|
2288
|
+
if (includeNull) return this.isRealNodeOrNull(node);
|
|
2289
|
+
return this.isRealNode(node);
|
|
2290
|
+
}, shouldProcessRoot = (node) => this.isRealNodeOrNull(node)) {
|
|
2291
|
+
startNode = this.ensureNode(startNode);
|
|
2292
|
+
if (!startNode) return [];
|
|
2293
|
+
const ans = [];
|
|
2294
|
+
if (iterationType === "RECURSIVE") {
|
|
2295
|
+
const dfs = (node) => {
|
|
2296
|
+
if (!shouldVisitRoot(node)) return;
|
|
2297
|
+
const visitLeft = () => {
|
|
2298
|
+
if (shouldVisitLeft(node) && (node == null ? void 0 : node.left) !== void 0) dfs(node == null ? void 0 : node.left);
|
|
2299
|
+
};
|
|
2300
|
+
const visitRight = () => {
|
|
2301
|
+
if (shouldVisitRight(node) && (node == null ? void 0 : node.right) !== void 0) dfs(node == null ? void 0 : node.right);
|
|
2302
|
+
};
|
|
2303
|
+
switch (pattern) {
|
|
2304
|
+
case "IN":
|
|
2305
|
+
visitLeft();
|
|
2306
|
+
if (shouldProcessRoot(node)) {
|
|
2307
|
+
ans.push(callback(node));
|
|
2308
|
+
if (onlyOne) return;
|
|
2309
|
+
}
|
|
2310
|
+
visitRight();
|
|
2311
|
+
break;
|
|
2312
|
+
case "PRE":
|
|
2313
|
+
if (shouldProcessRoot(node)) {
|
|
2314
|
+
ans.push(callback(node));
|
|
2315
|
+
if (onlyOne) return;
|
|
2316
|
+
}
|
|
2317
|
+
visitLeft();
|
|
2318
|
+
visitRight();
|
|
2319
|
+
break;
|
|
2320
|
+
case "POST":
|
|
2321
|
+
visitLeft();
|
|
2322
|
+
visitRight();
|
|
2323
|
+
if (shouldProcessRoot(node)) {
|
|
2324
|
+
ans.push(callback(node));
|
|
2325
|
+
if (onlyOne) return;
|
|
2326
|
+
}
|
|
2327
|
+
break;
|
|
2328
|
+
}
|
|
2329
|
+
};
|
|
2330
|
+
dfs(startNode);
|
|
2331
|
+
} else {
|
|
2332
|
+
const stack = [{ opt: 0 /* VISIT */, node: startNode }];
|
|
2333
|
+
const pushLeft = (cur) => {
|
|
2334
|
+
var _a;
|
|
2335
|
+
if (shouldVisitLeft(cur.node)) stack.push({ opt: 0 /* VISIT */, node: (_a = cur.node) == null ? void 0 : _a.left });
|
|
2336
|
+
};
|
|
2337
|
+
const pushRight = (cur) => {
|
|
2338
|
+
var _a;
|
|
2339
|
+
if (shouldVisitRight(cur.node)) stack.push({ opt: 0 /* VISIT */, node: (_a = cur.node) == null ? void 0 : _a.right });
|
|
2340
|
+
};
|
|
2341
|
+
const pushRoot = (cur) => {
|
|
2342
|
+
if (shouldVisitRoot(cur.node)) stack.push({ opt: 1 /* PROCESS */, node: cur.node });
|
|
2343
|
+
};
|
|
2344
|
+
while (stack.length > 0) {
|
|
2345
|
+
const cur = stack.pop();
|
|
2346
|
+
if (cur === void 0) continue;
|
|
2347
|
+
if (!shouldVisitRoot(cur.node)) continue;
|
|
2348
|
+
if (cur.opt === 1 /* PROCESS */) {
|
|
2349
|
+
if (shouldProcessRoot(cur.node) && cur.node !== void 0) {
|
|
2350
|
+
ans.push(callback(cur.node));
|
|
2351
|
+
if (onlyOne) return ans;
|
|
2352
|
+
}
|
|
2353
|
+
} else {
|
|
2354
|
+
switch (pattern) {
|
|
2355
|
+
case "IN":
|
|
2356
|
+
pushRight(cur);
|
|
2357
|
+
pushRoot(cur);
|
|
2358
|
+
pushLeft(cur);
|
|
2359
|
+
break;
|
|
2360
|
+
case "PRE":
|
|
2361
|
+
pushRight(cur);
|
|
2362
|
+
pushLeft(cur);
|
|
2363
|
+
pushRoot(cur);
|
|
2364
|
+
break;
|
|
2365
|
+
case "POST":
|
|
2366
|
+
pushRoot(cur);
|
|
2367
|
+
pushRight(cur);
|
|
2368
|
+
pushLeft(cur);
|
|
2369
|
+
break;
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
return ans;
|
|
2375
|
+
}
|
|
2376
|
+
/**
|
|
2377
|
+
* (Protected) Gets the iterator for the tree (default in-order).
|
|
2378
|
+
* @remarks Time O(N) for full iteration. O(H) to get the first element. Space O(H) for the iterative stack. O(H) for recursive stack.
|
|
2379
|
+
*
|
|
2380
|
+
* @param [node=this._root] - The node to start iteration from.
|
|
2381
|
+
* @returns An iterator for [key, value] pairs.
|
|
2382
|
+
*/
|
|
2383
|
+
*_getIterator(node = this._root) {
|
|
2384
|
+
if (!node) return;
|
|
2385
|
+
if (this.iterationType === "ITERATIVE") {
|
|
2386
|
+
const stack = [];
|
|
2387
|
+
let current = node;
|
|
2388
|
+
while (current || stack.length > 0) {
|
|
2389
|
+
while (this.isRealNode(current)) {
|
|
2390
|
+
stack.push(current);
|
|
2391
|
+
current = current.left;
|
|
2392
|
+
}
|
|
2393
|
+
current = stack.pop();
|
|
2394
|
+
if (this.isRealNode(current)) {
|
|
2395
|
+
if (this._isMapMode) yield [current.key, this._store.get(current.key)];
|
|
2396
|
+
else yield [current.key, current.value];
|
|
2397
|
+
current = current.right;
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
} else {
|
|
2401
|
+
if (node.left && this.isRealNode(node)) {
|
|
2402
|
+
yield* this[Symbol.iterator](node.left);
|
|
2403
|
+
}
|
|
2404
|
+
if (this._isMapMode) yield [node.key, this._store.get(node.key)];
|
|
2405
|
+
else yield [node.key, node.value];
|
|
2406
|
+
if (node.right && this.isRealNode(node)) {
|
|
2407
|
+
yield* this[Symbol.iterator](node.right);
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
/**
|
|
2412
|
+
* (Protected) Snapshots the current tree's configuration options.
|
|
2413
|
+
* @remarks Time O(1)
|
|
2414
|
+
*
|
|
2415
|
+
* @template TK, TV, TR - Generic types for the options.
|
|
2416
|
+
* @returns The options object.
|
|
2417
|
+
*/
|
|
2418
|
+
_snapshotOptions() {
|
|
2419
|
+
return {
|
|
2420
|
+
iterationType: this.iterationType,
|
|
2421
|
+
toEntryFn: this.toEntryFn,
|
|
2422
|
+
isMapMode: this.isMapMode,
|
|
2423
|
+
isDuplicate: this.isDuplicate
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2426
|
+
/**
|
|
2427
|
+
* (Protected) Creates a new, empty instance of the same tree constructor.
|
|
2428
|
+
* @remarks Time O(1)
|
|
2429
|
+
*
|
|
2430
|
+
* @template TK, TV, TR - Generic types for the new instance.
|
|
2431
|
+
* @param [options] - Options for the new tree.
|
|
2432
|
+
* @returns A new, empty tree.
|
|
2433
|
+
*/
|
|
2434
|
+
_createInstance(options) {
|
|
2435
|
+
const Ctor = this.constructor;
|
|
2436
|
+
return new Ctor([], { ...this._snapshotOptions(), ...options != null ? options : {} });
|
|
2437
|
+
}
|
|
2438
|
+
/**
|
|
2439
|
+
* (Protected) Creates a new instance of the same tree constructor, potentially with different generic types.
|
|
2440
|
+
* @remarks Time O(N) (or as per constructor) due to processing the iterable.
|
|
2441
|
+
*
|
|
2442
|
+
* @template TK, TV, TR - Generic types for the new instance.
|
|
2443
|
+
* @param [iter=[]] - An iterable to populate the new tree.
|
|
2444
|
+
* @param [options] - Options for the new tree.
|
|
2445
|
+
* @returns A new tree.
|
|
2446
|
+
*/
|
|
2447
|
+
_createLike(iter = [], options) {
|
|
2448
|
+
const Ctor = this.constructor;
|
|
2449
|
+
return new Ctor(iter, { ...this._snapshotOptions(), ...options != null ? options : {} });
|
|
2450
|
+
}
|
|
2451
|
+
/**
|
|
2452
|
+
* (Protected) Converts a key, node, or entry into a standardized [node, value] tuple.
|
|
2453
|
+
* @remarks Time O(1)
|
|
2454
|
+
*
|
|
2455
|
+
* @param keyNodeOrEntry - The input item.
|
|
2456
|
+
* @param [value] - An optional value (used if input is just a key).
|
|
2457
|
+
* @returns A tuple of [node, value].
|
|
2458
|
+
*/
|
|
2459
|
+
_keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value) {
|
|
2460
|
+
if (keyNodeOrEntry === void 0) return [void 0, void 0];
|
|
2461
|
+
if (keyNodeOrEntry === null) return [null, void 0];
|
|
2462
|
+
if (this.isNode(keyNodeOrEntry)) return [keyNodeOrEntry, value];
|
|
2463
|
+
if (this.isEntry(keyNodeOrEntry)) {
|
|
2464
|
+
const [key, entryValue] = keyNodeOrEntry;
|
|
2465
|
+
if (key === void 0) return [void 0, void 0];
|
|
2466
|
+
else if (key === null) return [null, void 0];
|
|
2467
|
+
const finalValue = value != null ? value : entryValue;
|
|
2468
|
+
return [this.createNode(key, finalValue), finalValue];
|
|
2469
|
+
}
|
|
2470
|
+
return [this.createNode(keyNodeOrEntry, value), value];
|
|
2471
|
+
}
|
|
2472
|
+
/**
|
|
2473
|
+
* (Protected) Helper for cloning. Performs a BFS and adds all nodes to the new tree.
|
|
2474
|
+
* @remarks Time O(N * M) (O(N) BFS + O(M) `add` for each node).
|
|
2475
|
+
*
|
|
2476
|
+
* @param cloned - The new, empty tree instance to populate.
|
|
2477
|
+
*/
|
|
2478
|
+
_clone(cloned) {
|
|
2479
|
+
this.bfs(
|
|
2480
|
+
(node) => {
|
|
2481
|
+
if (node === null) cloned.add(null);
|
|
2482
|
+
else {
|
|
2483
|
+
if (this._isMapMode) cloned.add([node.key, this._store.get(node.key)]);
|
|
2484
|
+
else cloned.add([node.key, node.value]);
|
|
2485
|
+
}
|
|
2486
|
+
},
|
|
2487
|
+
this._root,
|
|
2488
|
+
this.iterationType,
|
|
2489
|
+
true
|
|
2490
|
+
// Include nulls
|
|
2491
|
+
);
|
|
2492
|
+
if (this._isMapMode) cloned._store = this._store;
|
|
2493
|
+
}
|
|
2494
|
+
/**
|
|
2495
|
+
* (Protected) Recursive helper for `toVisual`.
|
|
2496
|
+
* @remarks Time O(N), Space O(N*H) or O(N^2)
|
|
2497
|
+
*
|
|
2498
|
+
* @param node - The current node.
|
|
2499
|
+
* @param options - Print options.
|
|
2500
|
+
* @returns Layout information for this subtree.
|
|
2501
|
+
*/
|
|
2502
|
+
_displayAux(node, options) {
|
|
2503
|
+
const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options;
|
|
2504
|
+
const emptyDisplayLayout = [["\u2500"], 1, 0, 0];
|
|
2505
|
+
if (node === null && !isShowNull) {
|
|
2506
|
+
return emptyDisplayLayout;
|
|
2507
|
+
} else if (node === void 0 && !isShowUndefined) {
|
|
2508
|
+
return emptyDisplayLayout;
|
|
2509
|
+
} else if (this.isNIL(node) && !isShowRedBlackNIL) {
|
|
2510
|
+
return emptyDisplayLayout;
|
|
2511
|
+
} else if (node !== null && node !== void 0) {
|
|
2512
|
+
const key = node.key, line = this.isNIL(node) ? "S" : String(key), width = line.length;
|
|
2513
|
+
return _buildNodeDisplay(
|
|
2514
|
+
line,
|
|
2515
|
+
width,
|
|
2516
|
+
this._displayAux(node.left, options),
|
|
2517
|
+
this._displayAux(node.right, options)
|
|
2518
|
+
);
|
|
2519
|
+
} else {
|
|
2520
|
+
const line = node === void 0 ? "U" : "N", width = line.length;
|
|
2521
|
+
return _buildNodeDisplay(line, width, [[""], 1, 0, 0], [[""], 1, 0, 0]);
|
|
2522
|
+
}
|
|
2523
|
+
function _buildNodeDisplay(line, width, left, right) {
|
|
2524
|
+
const [leftLines, leftWidth, leftHeight, leftMiddle] = left;
|
|
2525
|
+
const [rightLines, rightWidth, rightHeight, rightMiddle] = right;
|
|
2526
|
+
const firstLine = " ".repeat(Math.max(0, leftMiddle + 1)) + "_".repeat(Math.max(0, leftWidth - leftMiddle - 1)) + line + "_".repeat(Math.max(0, rightMiddle)) + " ".repeat(Math.max(0, rightWidth - rightMiddle));
|
|
2527
|
+
const secondLine = (leftHeight > 0 ? " ".repeat(leftMiddle) + "/" + " ".repeat(leftWidth - leftMiddle - 1) : " ".repeat(leftWidth)) + " ".repeat(width) + (rightHeight > 0 ? " ".repeat(rightMiddle) + "\\" + " ".repeat(rightWidth - rightMiddle - 1) : " ".repeat(rightWidth));
|
|
2528
|
+
const mergedLines = [firstLine, secondLine];
|
|
2529
|
+
for (let i = 0; i < Math.max(leftHeight, rightHeight); i++) {
|
|
2530
|
+
const leftLine = i < leftHeight ? leftLines[i] : " ".repeat(leftWidth);
|
|
2531
|
+
const rightLine = i < rightHeight ? rightLines[i] : " ".repeat(rightWidth);
|
|
2532
|
+
mergedLines.push(leftLine + " ".repeat(width) + rightLine);
|
|
2533
|
+
}
|
|
2534
|
+
return [
|
|
2535
|
+
mergedLines,
|
|
2536
|
+
leftWidth + width + rightWidth,
|
|
2537
|
+
Math.max(leftHeight, rightHeight) + 2,
|
|
2538
|
+
leftWidth + Math.floor(width / 2)
|
|
2539
|
+
];
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
/**
|
|
2543
|
+
* (Protected) Swaps the key/value properties of two nodes.
|
|
2544
|
+
* @remarks Time O(1)
|
|
2545
|
+
*
|
|
2546
|
+
* @param srcNode - The source node.
|
|
2547
|
+
* @param destNode - The destination node.
|
|
2548
|
+
* @returns The `destNode` (now holding `srcNode`'s properties).
|
|
2549
|
+
*/
|
|
2550
|
+
_swapProperties(srcNode, destNode) {
|
|
2551
|
+
srcNode = this.ensureNode(srcNode);
|
|
2552
|
+
destNode = this.ensureNode(destNode);
|
|
2553
|
+
if (srcNode && destNode) {
|
|
2554
|
+
const { key, value } = destNode;
|
|
2555
|
+
const tempNode = this.createNode(key, value);
|
|
2556
|
+
if (tempNode) {
|
|
2557
|
+
destNode.key = srcNode.key;
|
|
2558
|
+
if (!this._isMapMode) destNode.value = srcNode.value;
|
|
2559
|
+
srcNode.key = tempNode.key;
|
|
2560
|
+
if (!this._isMapMode) srcNode.value = tempNode.value;
|
|
2561
|
+
}
|
|
2562
|
+
return destNode;
|
|
2563
|
+
}
|
|
2564
|
+
return void 0;
|
|
2565
|
+
}
|
|
2566
|
+
/**
|
|
2567
|
+
* (Protected) Replaces a node in the tree with a new node, maintaining children and parent links.
|
|
2568
|
+
* @remarks Time O(1)
|
|
2569
|
+
*
|
|
2570
|
+
* @param oldNode - The node to be replaced.
|
|
2571
|
+
* @param newNode - The node to insert.
|
|
2572
|
+
* @returns The `newNode`.
|
|
2573
|
+
*/
|
|
2574
|
+
_replaceNode(oldNode, newNode) {
|
|
2575
|
+
if (oldNode.parent) {
|
|
2576
|
+
if (oldNode.parent.left === oldNode) {
|
|
2577
|
+
oldNode.parent.left = newNode;
|
|
2578
|
+
} else if (oldNode.parent.right === oldNode) {
|
|
2579
|
+
oldNode.parent.right = newNode;
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
newNode.left = oldNode.left;
|
|
2583
|
+
newNode.right = oldNode.right;
|
|
2584
|
+
newNode.parent = oldNode.parent;
|
|
2585
|
+
if (this._root === oldNode) {
|
|
2586
|
+
this._setRoot(newNode);
|
|
2587
|
+
}
|
|
2588
|
+
return newNode;
|
|
2589
|
+
}
|
|
2590
|
+
/**
|
|
2591
|
+
* (Protected) Sets the root node and clears its parent reference.
|
|
2592
|
+
* @remarks Time O(1)
|
|
2593
|
+
*
|
|
2594
|
+
* @param v - The node to set as root.
|
|
2595
|
+
*/
|
|
2596
|
+
_setRoot(v) {
|
|
2597
|
+
if (v) {
|
|
2598
|
+
v.parent = void 0;
|
|
2599
|
+
}
|
|
2600
|
+
this._root = v;
|
|
2601
|
+
}
|
|
2602
|
+
_ensurePredicate(keyNodeEntryOrPredicate) {
|
|
2603
|
+
if (keyNodeEntryOrPredicate === null || keyNodeEntryOrPredicate === void 0)
|
|
2604
|
+
return (node) => node ? false : false;
|
|
2605
|
+
if (this._isPredicate(keyNodeEntryOrPredicate)) return keyNodeEntryOrPredicate;
|
|
2606
|
+
if (this.isRealNode(keyNodeEntryOrPredicate))
|
|
2607
|
+
return (node) => node === keyNodeEntryOrPredicate;
|
|
2608
|
+
if (this.isEntry(keyNodeEntryOrPredicate)) {
|
|
2609
|
+
const [key] = keyNodeEntryOrPredicate;
|
|
2610
|
+
return (node) => {
|
|
2611
|
+
if (!node) return false;
|
|
2612
|
+
return node.key === key;
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
return (node) => {
|
|
2616
|
+
if (!node) return false;
|
|
2617
|
+
return node.key === keyNodeEntryOrPredicate;
|
|
2618
|
+
};
|
|
2619
|
+
}
|
|
2620
|
+
/**
|
|
2621
|
+
* (Protected) Checks if an item is a predicate function.
|
|
2622
|
+
* @remarks Time O(1)
|
|
2623
|
+
*
|
|
2624
|
+
* @param p - The item to check.
|
|
2625
|
+
* @returns True if it's a function.
|
|
2626
|
+
*/
|
|
2627
|
+
_isPredicate(p) {
|
|
2628
|
+
return typeof p === "function";
|
|
2629
|
+
}
|
|
2630
|
+
/**
|
|
2631
|
+
* (Protected) Extracts the key from a key, node, or entry.
|
|
2632
|
+
* @remarks Time O(1)
|
|
2633
|
+
*
|
|
2634
|
+
* @param keyNodeOrEntry - The item.
|
|
2635
|
+
* @returns The extracted key.
|
|
2636
|
+
*/
|
|
2637
|
+
_extractKey(keyNodeOrEntry) {
|
|
2638
|
+
if (keyNodeOrEntry === null) return null;
|
|
2639
|
+
if (keyNodeOrEntry === void 0) return;
|
|
2640
|
+
if (keyNodeOrEntry === this._NIL) return;
|
|
2641
|
+
if (this.isNode(keyNodeOrEntry)) return keyNodeOrEntry.key;
|
|
2642
|
+
if (this.isEntry(keyNodeOrEntry)) return keyNodeOrEntry[0];
|
|
2643
|
+
return keyNodeOrEntry;
|
|
2644
|
+
}
|
|
2645
|
+
/**
|
|
2646
|
+
* (Protected) Sets a value in the external store (Map mode).
|
|
2647
|
+
* @remarks Time O(1) (average for Map.set).
|
|
2648
|
+
*
|
|
2649
|
+
* @param key - The key.
|
|
2650
|
+
* @param value - The value.
|
|
2651
|
+
* @returns True if successful.
|
|
2652
|
+
*/
|
|
2653
|
+
_setValue(key, value) {
|
|
2654
|
+
if (key === null || key === void 0) return false;
|
|
2655
|
+
if (value === void 0) return false;
|
|
2656
|
+
return this._store.set(key, value);
|
|
2657
|
+
}
|
|
2658
|
+
/**
|
|
2659
|
+
* (Protected) Clears all nodes from the tree.
|
|
2660
|
+
* @remarks Time O(1)
|
|
2661
|
+
*/
|
|
2662
|
+
_clearNodes() {
|
|
2663
|
+
this._setRoot(void 0);
|
|
2664
|
+
this._size = 0;
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2667
|
+
* (Protected) Clears all values from the external store.
|
|
2668
|
+
* @remarks Time O(N)
|
|
2669
|
+
*/
|
|
2670
|
+
_clearValues() {
|
|
2671
|
+
this._store.clear();
|
|
2672
|
+
}
|
|
2673
|
+
};
|
|
2674
|
+
|
|
2675
|
+
// src/data-structures/binary-tree/bst.ts
|
|
2676
|
+
var BSTNode = class extends BinaryTreeNode {
|
|
2677
|
+
/**
|
|
2678
|
+
* Creates an instance of BSTNode.
|
|
2679
|
+
* @remarks Time O(1), Space O(1)
|
|
2680
|
+
*
|
|
2681
|
+
* @param key - The key of the node.
|
|
2682
|
+
* @param [value] - The value associated with the key.
|
|
2683
|
+
*/
|
|
2684
|
+
constructor(key, value) {
|
|
2685
|
+
super(key, value);
|
|
2686
|
+
__publicField(this, "parent");
|
|
2687
|
+
__publicField(this, "_left");
|
|
2688
|
+
__publicField(this, "_right");
|
|
2689
|
+
}
|
|
2690
|
+
/**
|
|
2691
|
+
* Gets the left child of the node.
|
|
2692
|
+
* @remarks Time O(1), Space O(1)
|
|
2693
|
+
*
|
|
2694
|
+
* @returns The left child.
|
|
2695
|
+
*/
|
|
2696
|
+
get left() {
|
|
2697
|
+
return this._left;
|
|
2698
|
+
}
|
|
2699
|
+
/**
|
|
2700
|
+
* Sets the left child of the node and updates its parent reference.
|
|
2701
|
+
* @remarks Time O(1), Space O(1)
|
|
2702
|
+
*
|
|
2703
|
+
* @param v - The node to set as the left child.
|
|
2704
|
+
*/
|
|
2705
|
+
set left(v) {
|
|
2706
|
+
if (v) v.parent = this;
|
|
2707
|
+
this._left = v;
|
|
2708
|
+
}
|
|
2709
|
+
/**
|
|
2710
|
+
* Gets the right child of the node.
|
|
2711
|
+
* @remarks Time O(1), Space O(1)
|
|
2712
|
+
*
|
|
2713
|
+
* @returns The right child.
|
|
2714
|
+
*/
|
|
2715
|
+
get right() {
|
|
2716
|
+
return this._right;
|
|
2717
|
+
}
|
|
2718
|
+
/**
|
|
2719
|
+
* Sets the right child of the node and updates its parent reference.
|
|
2720
|
+
* @remarks Time O(1), Space O(1)
|
|
2721
|
+
*
|
|
2722
|
+
* @param v - The node to set as the right child.
|
|
2723
|
+
*/
|
|
2724
|
+
set right(v) {
|
|
2725
|
+
if (v) v.parent = this;
|
|
2726
|
+
this._right = v;
|
|
2727
|
+
}
|
|
2728
|
+
};
|
|
2729
|
+
var BST = class extends BinaryTree {
|
|
2730
|
+
/**
|
|
2731
|
+
* Creates an instance of BST.
|
|
2732
|
+
* @remarks Time O(N log N) or O(N^2) depending on `isBalanceAdd` in `addMany` and input order. Space O(N).
|
|
2733
|
+
*
|
|
2734
|
+
* @param [keysNodesEntriesOrRaws=[]] - An iterable of items to add.
|
|
2735
|
+
* @param [options] - Configuration options for the BST, including comparator.
|
|
2736
|
+
*/
|
|
2737
|
+
constructor(keysNodesEntriesOrRaws = [], options) {
|
|
2738
|
+
super([], options);
|
|
2739
|
+
__publicField(this, "_root");
|
|
2740
|
+
__publicField(this, "_isReverse", false);
|
|
2741
|
+
/**
|
|
2742
|
+
* The default comparator function.
|
|
2743
|
+
* @remarks Time O(1) (or O(C) if `specifyComparable` is used, C is complexity of that function).
|
|
2744
|
+
*/
|
|
2745
|
+
__publicField(this, "_comparator", (a, b) => {
|
|
2746
|
+
if (isComparable(a) && isComparable(b)) {
|
|
2747
|
+
if (a > b) return 1;
|
|
2748
|
+
if (a < b) return -1;
|
|
2749
|
+
return 0;
|
|
2750
|
+
}
|
|
2751
|
+
if (this._specifyComparable) {
|
|
2752
|
+
const va = this._specifyComparable(a);
|
|
2753
|
+
const vb = this._specifyComparable(b);
|
|
2754
|
+
if (va > vb) return 1;
|
|
2755
|
+
if (va < vb) return -1;
|
|
2756
|
+
return 0;
|
|
2757
|
+
}
|
|
2758
|
+
if (typeof a === "object" || typeof b === "object") {
|
|
2759
|
+
throw TypeError(
|
|
2760
|
+
`When comparing object types, a custom specifyComparable must be defined in the constructor's options.`
|
|
2761
|
+
);
|
|
2762
|
+
}
|
|
2763
|
+
return 0;
|
|
2764
|
+
});
|
|
2765
|
+
__publicField(this, "_specifyComparable");
|
|
2766
|
+
if (options) {
|
|
2767
|
+
const { specifyComparable, isReverse } = options;
|
|
2768
|
+
if (typeof specifyComparable === "function") this._specifyComparable = specifyComparable;
|
|
2769
|
+
if (isReverse !== void 0) this._isReverse = isReverse;
|
|
2770
|
+
}
|
|
2771
|
+
if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
|
|
2772
|
+
}
|
|
2773
|
+
/**
|
|
2774
|
+
* Gets the root node of the tree.
|
|
2775
|
+
* @remarks Time O(1)
|
|
2776
|
+
*
|
|
2777
|
+
* @returns The root node.
|
|
2778
|
+
*/
|
|
2779
|
+
get root() {
|
|
2780
|
+
return this._root;
|
|
2781
|
+
}
|
|
2782
|
+
/**
|
|
2783
|
+
* Gets whether the tree's comparison logic is reversed.
|
|
2784
|
+
* @remarks Time O(1)
|
|
2785
|
+
*
|
|
2786
|
+
* @returns True if the tree is reversed (e.g., a max-heap logic).
|
|
2787
|
+
*/
|
|
2788
|
+
get isReverse() {
|
|
2789
|
+
return this._isReverse;
|
|
2790
|
+
}
|
|
2791
|
+
/**
|
|
2792
|
+
* Gets the comparator function used by the tree.
|
|
2793
|
+
* @remarks Time O(1)
|
|
2794
|
+
*
|
|
2795
|
+
* @returns The comparator function.
|
|
2796
|
+
*/
|
|
2797
|
+
get comparator() {
|
|
2798
|
+
return this._comparator;
|
|
2799
|
+
}
|
|
2800
|
+
/**
|
|
2801
|
+
* Gets the function used to extract a comparable value from a complex key.
|
|
2802
|
+
* @remarks Time O(1)
|
|
2803
|
+
*
|
|
2804
|
+
* @returns The key-to-comparable conversion function.
|
|
2805
|
+
*/
|
|
2806
|
+
get specifyComparable() {
|
|
2807
|
+
return this._specifyComparable;
|
|
2808
|
+
}
|
|
2809
|
+
/**
|
|
2810
|
+
* (Protected) Creates a new BST node.
|
|
2811
|
+
* @remarks Time O(1), Space O(1)
|
|
2812
|
+
*
|
|
2813
|
+
* @param key - The key for the new node.
|
|
2814
|
+
* @param [value] - The value for the new node (used if not in Map mode).
|
|
2815
|
+
* @returns The newly created BSTNode.
|
|
2816
|
+
*/
|
|
2817
|
+
createNode(key, value) {
|
|
2818
|
+
return new BSTNode(key, this._isMapMode ? void 0 : value);
|
|
2819
|
+
}
|
|
2820
|
+
/**
|
|
2821
|
+
* Ensures the input is a node. If it's a key or entry, it searches for the node.
|
|
2822
|
+
* @remarks Time O(log N) (height of the tree), O(N) worst-case.
|
|
2823
|
+
*
|
|
2824
|
+
* @param keyNodeOrEntry - The item to resolve to a node.
|
|
2825
|
+
* @param [iterationType=this.iterationType] - The traversal method to use if searching.
|
|
2826
|
+
* @returns The resolved node, or undefined if not found.
|
|
2827
|
+
*/
|
|
2828
|
+
ensureNode(keyNodeOrEntry, iterationType = this.iterationType) {
|
|
2829
|
+
var _a;
|
|
2830
|
+
return (_a = super.ensureNode(keyNodeOrEntry, iterationType)) != null ? _a : void 0;
|
|
2831
|
+
}
|
|
2832
|
+
/**
|
|
2833
|
+
* Checks if the given item is a `BSTNode` instance.
|
|
2834
|
+
* @remarks Time O(1), Space O(1)
|
|
2835
|
+
*
|
|
2836
|
+
* @param keyNodeOrEntry - The item to check.
|
|
2837
|
+
* @returns True if it's a BSTNode, false otherwise.
|
|
2838
|
+
*/
|
|
2839
|
+
isNode(keyNodeOrEntry) {
|
|
2840
|
+
return keyNodeOrEntry instanceof BSTNode;
|
|
2841
|
+
}
|
|
2842
|
+
/**
|
|
2843
|
+
* Checks if the given key is valid (comparable).
|
|
2844
|
+
* @remarks Time O(1)
|
|
2845
|
+
*
|
|
2846
|
+
* @param key - The key to validate.
|
|
2847
|
+
* @returns True if the key is valid, false otherwise.
|
|
2848
|
+
*/
|
|
2849
|
+
isValidKey(key) {
|
|
2850
|
+
return isComparable(key, this._specifyComparable !== void 0);
|
|
2851
|
+
}
|
|
2852
|
+
/**
|
|
2853
|
+
* Performs a Depth-First Search (DFS) traversal.
|
|
2854
|
+
* @remarks Time O(N), visits every node. Space O(log N) for the call/explicit stack. O(N) worst-case.
|
|
2855
|
+
*
|
|
2856
|
+
* @template C - The type of the callback function.
|
|
2857
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
|
|
2858
|
+
* @param [pattern='IN'] - The traversal order ('IN', 'PRE', 'POST').
|
|
2859
|
+
* @param [onlyOne=false] - If true, stops after the first callback.
|
|
2860
|
+
* @param [startNode=this._root] - The node to start from.
|
|
2861
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
2862
|
+
* @returns An array of callback results.
|
|
2863
|
+
*/
|
|
2864
|
+
dfs(callback = this._DEFAULT_NODE_CALLBACK, pattern = "IN", onlyOne = false, startNode = this._root, iterationType = this.iterationType) {
|
|
2865
|
+
return super.dfs(callback, pattern, onlyOne, startNode, iterationType);
|
|
2866
|
+
}
|
|
2867
|
+
/**
|
|
2868
|
+
* Performs a Breadth-First Search (BFS) or Level-Order traversal.
|
|
2869
|
+
* @remarks Time O(N), visits every node. Space O(N) in the worst case for the queue.
|
|
2870
|
+
*
|
|
2871
|
+
* @template C - The type of the callback function.
|
|
2872
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
|
|
2873
|
+
* @param [startNode=this._root] - The node to start from.
|
|
2874
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
2875
|
+
* @returns An array of callback results.
|
|
2876
|
+
*/
|
|
2877
|
+
bfs(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
2878
|
+
return super.bfs(callback, startNode, iterationType, false);
|
|
2879
|
+
}
|
|
2880
|
+
/**
|
|
2881
|
+
* Returns a 2D array of nodes, grouped by level.
|
|
2882
|
+
* @remarks Time O(N), visits every node. Space O(N) for the result array and the queue/stack.
|
|
2883
|
+
*
|
|
2884
|
+
* @template C - The type of the callback function.
|
|
2885
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
|
|
2886
|
+
* @param [startNode=this._root] - The node to start from.
|
|
2887
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
2888
|
+
* @returns A 2D array of callback results.
|
|
2889
|
+
*/
|
|
2890
|
+
listLevels(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
2891
|
+
return super.listLevels(callback, startNode, iterationType, false);
|
|
2892
|
+
}
|
|
2893
|
+
/**
|
|
2894
|
+
* Gets the first node matching a predicate.
|
|
2895
|
+
* @remarks Time O(log N) if searching by key, O(N) if searching by predicate. Space O(log N) or O(N).
|
|
2896
|
+
*
|
|
2897
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
2898
|
+
* @param [startNode=this._root] - The node to start the search from.
|
|
2899
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
2900
|
+
* @returns The first matching node, or undefined if not found.
|
|
2901
|
+
*/
|
|
2902
|
+
getNode(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
|
|
2903
|
+
var _a;
|
|
2904
|
+
return (_a = this.getNodes(keyNodeEntryOrPredicate, true, startNode, iterationType)[0]) != null ? _a : void 0;
|
|
2905
|
+
}
|
|
2906
|
+
/**
|
|
2907
|
+
* Searches the tree for nodes matching a predicate, key, or range.
|
|
2908
|
+
* @remarks This is an optimized search for a BST. If searching by key or range, it prunes branches.
|
|
2909
|
+
* Time O(H + M) for key/range search (H=height, M=matches). O(N) for predicate search.
|
|
2910
|
+
* Space O(log N) for the stack.
|
|
2911
|
+
*
|
|
2912
|
+
* @template C - The type of the callback function.
|
|
2913
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, predicate, or range to search for.
|
|
2914
|
+
* @param [onlyOne=false] - If true, stops after finding the first match.
|
|
2915
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on matching nodes.
|
|
2916
|
+
* @param [startNode=this._root] - The node to start the search from.
|
|
2917
|
+
* @param [iterationType=this.iterationType] - Whether to use 'RECURSIVE' or 'ITERATIVE' search.
|
|
2918
|
+
* @returns An array of results from the callback function for each matching node.
|
|
2919
|
+
*/
|
|
2920
|
+
search(keyNodeEntryOrPredicate, onlyOne = false, callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
2921
|
+
if (keyNodeEntryOrPredicate === void 0) return [];
|
|
2922
|
+
if (keyNodeEntryOrPredicate === null) return [];
|
|
2923
|
+
startNode = this.ensureNode(startNode);
|
|
2924
|
+
if (!startNode) return [];
|
|
2925
|
+
let predicate;
|
|
2926
|
+
const isRange = this.isRange(keyNodeEntryOrPredicate);
|
|
2927
|
+
if (isRange) {
|
|
2928
|
+
predicate = (node) => {
|
|
2929
|
+
if (!node) return false;
|
|
2930
|
+
return keyNodeEntryOrPredicate.isInRange(node.key, this._comparator);
|
|
2931
|
+
};
|
|
2932
|
+
} else {
|
|
2933
|
+
predicate = this._ensurePredicate(keyNodeEntryOrPredicate);
|
|
2934
|
+
}
|
|
2935
|
+
const shouldVisitLeft = (cur) => {
|
|
2936
|
+
if (!cur) return false;
|
|
2937
|
+
if (!this.isRealNode(cur.left)) return false;
|
|
2938
|
+
if (isRange) {
|
|
2939
|
+
const range = keyNodeEntryOrPredicate;
|
|
2940
|
+
const leftS = this.isReverse ? range.high : range.low;
|
|
2941
|
+
const leftI = this.isReverse ? range.includeHigh : range.includeLow;
|
|
2942
|
+
return leftI && this._compare(cur.key, leftS) >= 0 || !leftI && this._compare(cur.key, leftS) > 0;
|
|
2943
|
+
}
|
|
2944
|
+
if (!isRange && !this._isPredicate(keyNodeEntryOrPredicate)) {
|
|
2945
|
+
const benchmarkKey = this._extractKey(keyNodeEntryOrPredicate);
|
|
2946
|
+
return benchmarkKey !== null && benchmarkKey !== void 0 && this._compare(cur.key, benchmarkKey) > 0;
|
|
2947
|
+
}
|
|
2948
|
+
return true;
|
|
2949
|
+
};
|
|
2950
|
+
const shouldVisitRight = (cur) => {
|
|
2951
|
+
if (!cur) return false;
|
|
2952
|
+
if (!this.isRealNode(cur.right)) return false;
|
|
2953
|
+
if (isRange) {
|
|
2954
|
+
const range = keyNodeEntryOrPredicate;
|
|
2955
|
+
const rightS = this.isReverse ? range.low : range.high;
|
|
2956
|
+
const rightI = this.isReverse ? range.includeLow : range.includeHigh;
|
|
2957
|
+
return rightI && this._compare(cur.key, rightS) <= 0 || !rightI && this._compare(cur.key, rightS) < 0;
|
|
2958
|
+
}
|
|
2959
|
+
if (!isRange && !this._isPredicate(keyNodeEntryOrPredicate)) {
|
|
2960
|
+
const benchmarkKey = this._extractKey(keyNodeEntryOrPredicate);
|
|
2961
|
+
return benchmarkKey !== null && benchmarkKey !== void 0 && this._compare(cur.key, benchmarkKey) < 0;
|
|
2962
|
+
}
|
|
2963
|
+
return true;
|
|
2964
|
+
};
|
|
2965
|
+
return super._dfs(
|
|
2966
|
+
callback,
|
|
2967
|
+
"IN",
|
|
2968
|
+
// In-order is efficient for range/key search
|
|
2969
|
+
onlyOne,
|
|
2970
|
+
startNode,
|
|
2971
|
+
iterationType,
|
|
2972
|
+
false,
|
|
2973
|
+
shouldVisitLeft,
|
|
2974
|
+
shouldVisitRight,
|
|
2975
|
+
() => true,
|
|
2976
|
+
// shouldVisitRoot (always visit)
|
|
2977
|
+
(cur) => !!cur && predicate(cur)
|
|
2978
|
+
// shouldProcessRoot (only process if predicate matches)
|
|
2979
|
+
);
|
|
2980
|
+
}
|
|
2981
|
+
/**
|
|
2982
|
+
* Performs an optimized search for nodes within a given key range.
|
|
2983
|
+
* @remarks Time O(H + M), where H is tree height and M is the number of matches.
|
|
2984
|
+
*
|
|
2985
|
+
* @template C - The type of the callback function.
|
|
2986
|
+
* @param range - A `Range` object or a `[low, high]` tuple.
|
|
2987
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on matching nodes.
|
|
2988
|
+
* @param [startNode=this._root] - The node to start the search from.
|
|
2989
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
2990
|
+
* @returns An array of callback results.
|
|
2991
|
+
*/
|
|
2992
|
+
rangeSearch(range, callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
2993
|
+
const searchRange = range instanceof Range ? range : new Range(range[0], range[1]);
|
|
2994
|
+
return this.search(searchRange, false, callback, startNode, iterationType);
|
|
2995
|
+
}
|
|
2996
|
+
/**
|
|
2997
|
+
* Adds a new node to the BST based on key comparison.
|
|
2998
|
+
* @remarks Time O(log N), where H is tree height. O(N) worst-case (unbalanced tree), O(log N) average. Space O(1).
|
|
2999
|
+
*
|
|
3000
|
+
* @param keyNodeOrEntry - The key, node, or entry to add.
|
|
3001
|
+
* @param [value] - The value, if providing just a key.
|
|
3002
|
+
* @returns True if the addition was successful, false otherwise.
|
|
3003
|
+
*/
|
|
3004
|
+
add(keyNodeOrEntry, value) {
|
|
3005
|
+
const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
|
3006
|
+
if (newNode === void 0) return false;
|
|
3007
|
+
if (this._root === void 0) {
|
|
3008
|
+
this._setRoot(newNode);
|
|
3009
|
+
if (this._isMapMode) this._setValue(newNode == null ? void 0 : newNode.key, newValue);
|
|
3010
|
+
this._size++;
|
|
3011
|
+
return true;
|
|
3012
|
+
}
|
|
3013
|
+
let current = this._root;
|
|
3014
|
+
while (current !== void 0) {
|
|
3015
|
+
if (this._compare(current.key, newNode.key) === 0) {
|
|
3016
|
+
this._replaceNode(current, newNode);
|
|
3017
|
+
if (this._isMapMode) this._setValue(current.key, newValue);
|
|
3018
|
+
return true;
|
|
3019
|
+
} else if (this._compare(current.key, newNode.key) > 0) {
|
|
3020
|
+
if (current.left === void 0) {
|
|
3021
|
+
current.left = newNode;
|
|
3022
|
+
if (this._isMapMode) this._setValue(newNode == null ? void 0 : newNode.key, newValue);
|
|
3023
|
+
this._size++;
|
|
3024
|
+
return true;
|
|
3025
|
+
}
|
|
3026
|
+
if (current.left !== null) current = current.left;
|
|
3027
|
+
} else {
|
|
3028
|
+
if (current.right === void 0) {
|
|
3029
|
+
current.right = newNode;
|
|
3030
|
+
if (this._isMapMode) this._setValue(newNode == null ? void 0 : newNode.key, newValue);
|
|
3031
|
+
this._size++;
|
|
3032
|
+
return true;
|
|
3033
|
+
}
|
|
3034
|
+
if (current.right !== null) current = current.right;
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
return false;
|
|
3038
|
+
}
|
|
3039
|
+
/**
|
|
3040
|
+
* Adds multiple items to the tree.
|
|
3041
|
+
* @remarks If `isBalanceAdd` is true, sorts the input and builds a balanced tree. Time O(N log N) (due to sort and balanced add).
|
|
3042
|
+
* If false, adds items one by one. Time O(N * H), which is O(N^2) worst-case.
|
|
3043
|
+
* Space O(N) for sorting and recursion/iteration stack.
|
|
3044
|
+
*
|
|
3045
|
+
* @param keysNodesEntriesOrRaws - An iterable of items to add.
|
|
3046
|
+
* @param [values] - An optional parallel iterable of values.
|
|
3047
|
+
* @param [isBalanceAdd=true] - If true, builds a balanced tree from the items.
|
|
3048
|
+
* @param [iterationType=this.iterationType] - The traversal method for balanced add (recursive or iterative).
|
|
3049
|
+
* @returns An array of booleans indicating the success of each individual `add` operation.
|
|
3050
|
+
*/
|
|
3051
|
+
addMany(keysNodesEntriesOrRaws, values, isBalanceAdd = true, iterationType = this.iterationType) {
|
|
3052
|
+
const inserted = [];
|
|
3053
|
+
const valuesIterator = values == null ? void 0 : values[Symbol.iterator]();
|
|
3054
|
+
if (!isBalanceAdd) {
|
|
3055
|
+
for (let kve of keysNodesEntriesOrRaws) {
|
|
3056
|
+
const val = valuesIterator == null ? void 0 : valuesIterator.next().value;
|
|
3057
|
+
if (this.isRaw(kve)) kve = this._toEntryFn(kve);
|
|
3058
|
+
inserted.push(this.add(kve, val));
|
|
3059
|
+
}
|
|
3060
|
+
return inserted;
|
|
3061
|
+
}
|
|
3062
|
+
const realBTNExemplars = [];
|
|
3063
|
+
let i = 0;
|
|
3064
|
+
for (const kve of keysNodesEntriesOrRaws) {
|
|
3065
|
+
realBTNExemplars.push({ key: kve, value: valuesIterator == null ? void 0 : valuesIterator.next().value, orgIndex: i++ });
|
|
3066
|
+
}
|
|
3067
|
+
const sorted = realBTNExemplars.sort(({ key: a }, { key: b }) => {
|
|
3068
|
+
let keyA, keyB;
|
|
3069
|
+
if (this.isRaw(a)) keyA = this._toEntryFn(a)[0];
|
|
3070
|
+
else if (this.isEntry(a)) keyA = a[0];
|
|
3071
|
+
else if (this.isRealNode(a)) keyA = a.key;
|
|
3072
|
+
else keyA = a;
|
|
3073
|
+
if (this.isRaw(b)) keyB = this._toEntryFn(b)[0];
|
|
3074
|
+
else if (this.isEntry(b)) keyB = b[0];
|
|
3075
|
+
else if (this.isRealNode(b)) keyB = b.key;
|
|
3076
|
+
else keyB = b;
|
|
3077
|
+
if (keyA != null && keyB != null) return this._compare(keyA, keyB);
|
|
3078
|
+
return 0;
|
|
3079
|
+
});
|
|
3080
|
+
const _dfs = (arr) => {
|
|
3081
|
+
if (arr.length === 0) return;
|
|
3082
|
+
const mid = Math.floor((arr.length - 1) / 2);
|
|
3083
|
+
const { key, value, orgIndex } = arr[mid];
|
|
3084
|
+
if (this.isRaw(key)) {
|
|
3085
|
+
const entry = this._toEntryFn(key);
|
|
3086
|
+
inserted[orgIndex] = this.add(entry);
|
|
3087
|
+
} else {
|
|
3088
|
+
inserted[orgIndex] = this.add(key, value);
|
|
3089
|
+
}
|
|
3090
|
+
_dfs(arr.slice(0, mid));
|
|
3091
|
+
_dfs(arr.slice(mid + 1));
|
|
3092
|
+
};
|
|
3093
|
+
const _iterate = () => {
|
|
3094
|
+
const n = sorted.length;
|
|
3095
|
+
const stack = [[0, n - 1]];
|
|
3096
|
+
while (stack.length > 0) {
|
|
3097
|
+
const popped = stack.pop();
|
|
3098
|
+
if (!popped) continue;
|
|
3099
|
+
const [l, r] = popped;
|
|
3100
|
+
if (l > r) continue;
|
|
3101
|
+
const m = l + Math.floor((r - l) / 2);
|
|
3102
|
+
const { key, value, orgIndex } = sorted[m];
|
|
3103
|
+
if (this.isRaw(key)) {
|
|
3104
|
+
const entry = this._toEntryFn(key);
|
|
3105
|
+
inserted[orgIndex] = this.add(entry);
|
|
3106
|
+
} else {
|
|
3107
|
+
inserted[orgIndex] = this.add(key, value);
|
|
3108
|
+
}
|
|
3109
|
+
stack.push([m + 1, r]);
|
|
3110
|
+
stack.push([l, m - 1]);
|
|
3111
|
+
}
|
|
3112
|
+
};
|
|
3113
|
+
if (iterationType === "RECURSIVE") _dfs(sorted);
|
|
3114
|
+
else _iterate();
|
|
3115
|
+
return inserted;
|
|
3116
|
+
}
|
|
3117
|
+
/**
|
|
3118
|
+
* Traverses the tree and returns nodes that are lesser or greater than a target node.
|
|
3119
|
+
* @remarks Time O(N), as it performs a full traversal. Space O(log N) or O(N).
|
|
3120
|
+
*
|
|
3121
|
+
* @template C - The type of the callback function.
|
|
3122
|
+
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on matching nodes.
|
|
3123
|
+
* @param [lesserOrGreater=-1] - -1 for lesser, 1 for greater, 0 for equal.
|
|
3124
|
+
* @param [targetNode=this._root] - The node to compare against.
|
|
3125
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
3126
|
+
* @returns An array of callback results.
|
|
3127
|
+
*/
|
|
3128
|
+
lesserOrGreaterTraverse(callback = this._DEFAULT_NODE_CALLBACK, lesserOrGreater = -1, targetNode = this._root, iterationType = this.iterationType) {
|
|
3129
|
+
const targetNodeEnsured = this.ensureNode(targetNode);
|
|
3130
|
+
const ans = [];
|
|
3131
|
+
if (!this._root || !targetNodeEnsured) return ans;
|
|
3132
|
+
const targetKey = targetNodeEnsured.key;
|
|
3133
|
+
if (iterationType === "RECURSIVE") {
|
|
3134
|
+
const dfs = (cur) => {
|
|
3135
|
+
const compared = this._compare(cur.key, targetKey);
|
|
3136
|
+
if (Math.sign(compared) == lesserOrGreater) ans.push(callback(cur));
|
|
3137
|
+
if (this.isRealNode(cur.left)) dfs(cur.left);
|
|
3138
|
+
if (this.isRealNode(cur.right)) dfs(cur.right);
|
|
3139
|
+
};
|
|
3140
|
+
dfs(this._root);
|
|
3141
|
+
return ans;
|
|
3142
|
+
} else {
|
|
3143
|
+
const queue = new Queue([this._root]);
|
|
3144
|
+
while (queue.length > 0) {
|
|
3145
|
+
const cur = queue.shift();
|
|
3146
|
+
if (this.isRealNode(cur)) {
|
|
3147
|
+
const compared = this._compare(cur.key, targetKey);
|
|
3148
|
+
if (Math.sign(compared) == lesserOrGreater) ans.push(callback(cur));
|
|
3149
|
+
if (this.isRealNode(cur.left)) queue.push(cur.left);
|
|
3150
|
+
if (this.isRealNode(cur.right)) queue.push(cur.right);
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
return ans;
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
/**
|
|
3157
|
+
* Rebuilds the tree to be perfectly balanced.
|
|
3158
|
+
* @remarks Time O(N) (O(N) for DFS, O(N) for sorted build). Space O(N) for node array and recursion stack.
|
|
3159
|
+
*
|
|
3160
|
+
* @param [iterationType=this.iterationType] - The traversal method for the initial node export.
|
|
3161
|
+
* @returns True if successful, false if the tree was empty.
|
|
3162
|
+
*/
|
|
3163
|
+
perfectlyBalance(iterationType = this.iterationType) {
|
|
3164
|
+
const nodes = this.dfs((node) => node, "IN", false, this._root, iterationType);
|
|
3165
|
+
const n = nodes.length;
|
|
3166
|
+
this._clearNodes();
|
|
3167
|
+
if (n === 0) return false;
|
|
3168
|
+
const build = (l, r, parent) => {
|
|
3169
|
+
if (l > r) return void 0;
|
|
3170
|
+
const m = l + (r - l >> 1);
|
|
3171
|
+
const root = nodes[m];
|
|
3172
|
+
const leftChild = build(l, m - 1, root);
|
|
3173
|
+
const rightChild = build(m + 1, r, root);
|
|
3174
|
+
root.left = leftChild;
|
|
3175
|
+
root.right = rightChild;
|
|
3176
|
+
root.parent = parent;
|
|
3177
|
+
return root;
|
|
3178
|
+
};
|
|
3179
|
+
const newRoot = build(0, n - 1, void 0);
|
|
3180
|
+
this._setRoot(newRoot);
|
|
3181
|
+
this._size = n;
|
|
3182
|
+
return true;
|
|
3183
|
+
}
|
|
3184
|
+
/**
|
|
3185
|
+
* Checks if the tree meets the AVL balance condition (height difference <= 1).
|
|
3186
|
+
* @remarks Time O(N), as it must visit every node to compute height. Space O(log N) for recursion or O(N) for iterative map.
|
|
3187
|
+
*
|
|
3188
|
+
* @param [iterationType=this.iterationType] - The traversal method.
|
|
3189
|
+
* @returns True if the tree is AVL balanced, false otherwise.
|
|
3190
|
+
*/
|
|
3191
|
+
isAVLBalanced(iterationType = this.iterationType) {
|
|
3192
|
+
if (!this._root) return true;
|
|
3193
|
+
let balanced = true;
|
|
3194
|
+
if (iterationType === "RECURSIVE") {
|
|
3195
|
+
const _height = (cur) => {
|
|
3196
|
+
if (!cur) return 0;
|
|
3197
|
+
const leftHeight = _height(cur.left);
|
|
3198
|
+
const rightHeight = _height(cur.right);
|
|
3199
|
+
if (Math.abs(leftHeight - rightHeight) > 1) balanced = false;
|
|
3200
|
+
return Math.max(leftHeight, rightHeight) + 1;
|
|
3201
|
+
};
|
|
3202
|
+
_height(this._root);
|
|
3203
|
+
} else {
|
|
3204
|
+
const stack = [];
|
|
3205
|
+
let node = this._root, last = void 0;
|
|
3206
|
+
const depths = /* @__PURE__ */ new Map();
|
|
3207
|
+
while (stack.length > 0 || node) {
|
|
3208
|
+
if (node) {
|
|
3209
|
+
stack.push(node);
|
|
3210
|
+
if (node.left !== null) node = node.left;
|
|
3211
|
+
} else {
|
|
3212
|
+
node = stack[stack.length - 1];
|
|
3213
|
+
if (!node.right || last === node.right) {
|
|
3214
|
+
node = stack.pop();
|
|
3215
|
+
if (node) {
|
|
3216
|
+
const left = node.left ? depths.get(node.left) : -1;
|
|
3217
|
+
const right = node.right ? depths.get(node.right) : -1;
|
|
3218
|
+
if (Math.abs(left - right) > 1) return false;
|
|
3219
|
+
depths.set(node, 1 + Math.max(left, right));
|
|
3220
|
+
last = node;
|
|
3221
|
+
node = void 0;
|
|
3222
|
+
}
|
|
3223
|
+
} else node = node.right;
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
return balanced;
|
|
3228
|
+
}
|
|
3229
|
+
/**
|
|
3230
|
+
* Creates a new BST by mapping each [key, value] pair to a new entry.
|
|
3231
|
+
* @remarks Time O(N * H), where N is nodes in this tree, and H is height of the new tree during insertion.
|
|
3232
|
+
* Space O(N) for the new tree.
|
|
3233
|
+
*
|
|
3234
|
+
* @template MK - New key type.
|
|
3235
|
+
* @template MV - New value type.
|
|
3236
|
+
* @template MR - New raw type.
|
|
3237
|
+
* @param callback - A function to map each [key, value] pair.
|
|
3238
|
+
* @param [options] - Options for the new BST.
|
|
3239
|
+
* @param [thisArg] - `this` context for the callback.
|
|
3240
|
+
* @returns A new, mapped BST.
|
|
3241
|
+
*/
|
|
3242
|
+
map(callback, options, thisArg) {
|
|
3243
|
+
const out = this._createLike([], options);
|
|
3244
|
+
let index = 0;
|
|
3245
|
+
for (const [key, value] of this) {
|
|
3246
|
+
out.add(callback.call(thisArg, key, value, index++, this));
|
|
3247
|
+
}
|
|
3248
|
+
return out;
|
|
3249
|
+
}
|
|
3250
|
+
/**
|
|
3251
|
+
* Deletes the first node found that satisfies the predicate.
|
|
3252
|
+
* @remarks Performs an in-order traversal. Time O(N) worst-case (O(log N) to find + O(log N) to delete). Space O(log N) for stack.
|
|
3253
|
+
*
|
|
3254
|
+
* @param predicate - A function to test each [key, value] pair.
|
|
3255
|
+
* @returns True if a node was deleted, false otherwise.
|
|
3256
|
+
*/
|
|
3257
|
+
deleteWhere(predicate) {
|
|
3258
|
+
const stack = [];
|
|
3259
|
+
let cur = this._root;
|
|
3260
|
+
let index = 0;
|
|
3261
|
+
while (stack.length > 0 || cur !== void 0) {
|
|
3262
|
+
while (cur !== void 0 && cur !== null) {
|
|
3263
|
+
stack.push(cur);
|
|
3264
|
+
cur = cur.left;
|
|
3265
|
+
}
|
|
3266
|
+
const node = stack.pop();
|
|
3267
|
+
if (!node) break;
|
|
3268
|
+
const key = node.key;
|
|
3269
|
+
const val = node.value;
|
|
3270
|
+
if (predicate(key, val, index++, this)) {
|
|
3271
|
+
return this._deleteByKey(key);
|
|
3272
|
+
}
|
|
3273
|
+
cur = node.right;
|
|
3274
|
+
}
|
|
3275
|
+
return false;
|
|
3276
|
+
}
|
|
3277
|
+
/**
|
|
3278
|
+
* (Protected) Creates a new, empty instance of the same BST constructor.
|
|
3279
|
+
* @remarks Time O(1)
|
|
3280
|
+
*
|
|
3281
|
+
* @template TK, TV, TR - Generic types for the new instance.
|
|
3282
|
+
* @param [options] - Options for the new BST.
|
|
3283
|
+
* @returns A new, empty BST.
|
|
3284
|
+
*/
|
|
3285
|
+
_createInstance(options) {
|
|
3286
|
+
const Ctor = this.constructor;
|
|
3287
|
+
return new Ctor([], { ...this._snapshotOptions(), ...options != null ? options : {} });
|
|
3288
|
+
}
|
|
3289
|
+
/**
|
|
3290
|
+
* (Protected) Creates a new instance of the same BST constructor, potentially with different generic types.
|
|
3291
|
+
* @remarks Time O(N log N) or O(N^2) (from constructor) due to processing the iterable.
|
|
3292
|
+
*
|
|
3293
|
+
* @template TK, TV, TR - Generic types for the new instance.
|
|
3294
|
+
* @param [iter=[]] - An iterable to populate the new BST.
|
|
3295
|
+
* @param [options] - Options for the new BST.
|
|
3296
|
+
* @returns A new BST.
|
|
3297
|
+
*/
|
|
3298
|
+
_createLike(iter = [], options) {
|
|
3299
|
+
const Ctor = this.constructor;
|
|
3300
|
+
return new Ctor(iter, { ...this._snapshotOptions(), ...options != null ? options : {} });
|
|
3301
|
+
}
|
|
3302
|
+
/**
|
|
3303
|
+
* (Protected) Snapshots the current BST's configuration options.
|
|
3304
|
+
* @remarks Time O(1)
|
|
3305
|
+
*
|
|
3306
|
+
* @template TK, TV, TR - Generic types for the options.
|
|
3307
|
+
* @returns The options object.
|
|
3308
|
+
*/
|
|
3309
|
+
_snapshotOptions() {
|
|
3310
|
+
return {
|
|
3311
|
+
...super._snapshotOptions(),
|
|
3312
|
+
specifyComparable: this.specifyComparable,
|
|
3313
|
+
isReverse: this.isReverse
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3316
|
+
/**
|
|
3317
|
+
* (Protected) Converts a key, node, or entry into a standardized [node, value] tuple.
|
|
3318
|
+
* @remarks Time O(1)
|
|
3319
|
+
*
|
|
3320
|
+
* @param keyNodeOrEntry - The input item.
|
|
3321
|
+
* @param [value] - An optional value (used if input is just a key).
|
|
3322
|
+
* @returns A tuple of [node, value].
|
|
3323
|
+
*/
|
|
3324
|
+
_keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value) {
|
|
3325
|
+
const [node, entryValue] = super._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
|
3326
|
+
if (node === null) return [void 0, void 0];
|
|
3327
|
+
return [node, value != null ? value : entryValue];
|
|
3328
|
+
}
|
|
3329
|
+
/**
|
|
3330
|
+
* (Protected) Sets the root node and clears its parent reference.
|
|
3331
|
+
* @remarks Time O(1)
|
|
3332
|
+
*
|
|
3333
|
+
* @param v - The node to set as root.
|
|
3334
|
+
*/
|
|
3335
|
+
_setRoot(v) {
|
|
3336
|
+
if (v) v.parent = void 0;
|
|
3337
|
+
this._root = v;
|
|
3338
|
+
}
|
|
3339
|
+
/**
|
|
3340
|
+
* (Protected) Compares two keys using the tree's comparator and reverse setting.
|
|
3341
|
+
* @remarks Time O(1) (or O(C) if `specifyComparable` is used).
|
|
3342
|
+
*
|
|
3343
|
+
* @param a - The first key.
|
|
3344
|
+
* @param b - The second key.
|
|
3345
|
+
* @returns A number (1, -1, or 0) representing the comparison.
|
|
3346
|
+
*/
|
|
3347
|
+
_compare(a, b) {
|
|
3348
|
+
return this._isReverse ? -this._comparator(a, b) : this._comparator(a, b);
|
|
3349
|
+
}
|
|
3350
|
+
/**
|
|
3351
|
+
* (Private) Deletes a node by its key.
|
|
3352
|
+
* @remarks Standard BST deletion algorithm. Time O(log N), O(N) worst-case. Space O(1).
|
|
3353
|
+
*
|
|
3354
|
+
* @param key - The key of the node to delete.
|
|
3355
|
+
* @returns True if the node was found and deleted, false otherwise.
|
|
3356
|
+
*/
|
|
3357
|
+
_deleteByKey(key) {
|
|
3358
|
+
var _a;
|
|
3359
|
+
let node = this._root;
|
|
3360
|
+
while (node) {
|
|
3361
|
+
const cmp = this._compare(node.key, key);
|
|
3362
|
+
if (cmp === 0) break;
|
|
3363
|
+
node = cmp > 0 ? node.left : node.right;
|
|
3364
|
+
}
|
|
3365
|
+
if (!node) return false;
|
|
3366
|
+
const transplant = (u, v) => {
|
|
3367
|
+
const p = u == null ? void 0 : u.parent;
|
|
3368
|
+
if (!p) {
|
|
3369
|
+
this._setRoot(v);
|
|
3370
|
+
} else if (p.left === u) {
|
|
3371
|
+
p.left = v;
|
|
3372
|
+
} else {
|
|
3373
|
+
p.right = v;
|
|
3374
|
+
}
|
|
3375
|
+
if (v) v.parent = p;
|
|
3376
|
+
};
|
|
3377
|
+
const minNode = (x) => {
|
|
3378
|
+
if (!x) return void 0;
|
|
3379
|
+
while (x.left !== void 0 && x.left !== null) x = x.left;
|
|
3380
|
+
return x;
|
|
3381
|
+
};
|
|
3382
|
+
if (node.left === void 0) {
|
|
3383
|
+
transplant(node, node.right);
|
|
3384
|
+
} else if (node.right === void 0) {
|
|
3385
|
+
transplant(node, node.left);
|
|
3386
|
+
} else {
|
|
3387
|
+
const succ = minNode(node.right);
|
|
3388
|
+
if (succ.parent !== node) {
|
|
3389
|
+
transplant(succ, succ.right);
|
|
3390
|
+
succ.right = node.right;
|
|
3391
|
+
if (succ.right) succ.right.parent = succ;
|
|
3392
|
+
}
|
|
3393
|
+
transplant(node, succ);
|
|
3394
|
+
succ.left = node.left;
|
|
3395
|
+
if (succ.left) succ.left.parent = succ;
|
|
3396
|
+
}
|
|
3397
|
+
this._size = Math.max(0, ((_a = this._size) != null ? _a : 0) - 1);
|
|
3398
|
+
return true;
|
|
3399
|
+
}
|
|
3400
|
+
};
|
|
3401
|
+
return __toCommonJS(src_exports);
|
|
3402
|
+
})();
|
|
3403
|
+
/**
|
|
3404
|
+
* data-structure-typed
|
|
3405
|
+
*
|
|
3406
|
+
* @author Pablo Zeng
|
|
3407
|
+
* @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
|
|
3408
|
+
* @license MIT License
|
|
3409
|
+
*/
|
|
3410
|
+
//# sourceMappingURL=bst-typed.js.map
|