stack-typed 2.1.1 → 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 +552 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/esm/index.mjs +548 -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 +11 -2
- package/dist/{interfaces → types/interfaces}/binary-tree.d.ts +1 -1
- 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/stack-typed.js +563 -0
- package/dist/umd/stack-typed.js.map +1 -0
- package/dist/umd/stack-typed.min.js +9 -0
- package/dist/umd/stack-typed.min.js.map +1 -0
- package/package.json +25 -5
- package/src/data-structures/binary-tree/avl-tree-counter.ts +4 -4
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +1 -1
- package/src/data-structures/binary-tree/avl-tree.ts +2 -2
- package/src/data-structures/binary-tree/binary-tree.ts +4 -4
- package/src/data-structures/binary-tree/bst.ts +1 -1
- package/src/data-structures/binary-tree/red-black-tree.ts +2 -2
- package/src/data-structures/binary-tree/tree-counter.ts +4 -4
- package/src/data-structures/binary-tree/tree-multi-map.ts +1 -1
- 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 +1 -1
- 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 -12
- package/dist/index.js +0 -28
- 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
|
@@ -1,1925 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* data-structure-typed
|
|
4
|
-
*
|
|
5
|
-
* @author Pablo Zeng
|
|
6
|
-
* @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
|
|
7
|
-
* @license MIT License
|
|
8
|
-
*/
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.BinaryTree = exports.BinaryTreeNode = void 0;
|
|
11
|
-
const utils_1 = require("../../utils");
|
|
12
|
-
const queue_1 = require("../queue");
|
|
13
|
-
const base_1 = require("../base");
|
|
14
|
-
const common_1 = require("../../common");
|
|
15
|
-
/**
|
|
16
|
-
* @template K - The type of the key.
|
|
17
|
-
* @template V - The type of the value.
|
|
18
|
-
*/
|
|
19
|
-
class BinaryTreeNode {
|
|
20
|
-
/**
|
|
21
|
-
* Creates an instance of BinaryTreeNode.
|
|
22
|
-
* @remarks Time O(1), Space O(1)
|
|
23
|
-
*
|
|
24
|
-
* @param key - The key of the node.
|
|
25
|
-
* @param [value] - The value associated with the key.
|
|
26
|
-
*/
|
|
27
|
-
constructor(key, value) {
|
|
28
|
-
this.parent = undefined;
|
|
29
|
-
this._left = undefined;
|
|
30
|
-
this._right = undefined;
|
|
31
|
-
this._height = 0;
|
|
32
|
-
this._color = 'BLACK';
|
|
33
|
-
this._count = 1;
|
|
34
|
-
this.key = key;
|
|
35
|
-
this.value = value;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Gets the left child of the node.
|
|
39
|
-
* @remarks Time O(1), Space O(1)
|
|
40
|
-
*
|
|
41
|
-
* @returns The left child.
|
|
42
|
-
*/
|
|
43
|
-
get left() {
|
|
44
|
-
return this._left;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Sets the left child of the node and updates its parent reference.
|
|
48
|
-
* @remarks Time O(1), Space O(1)
|
|
49
|
-
*
|
|
50
|
-
* @param v - The node to set as the left child.
|
|
51
|
-
*/
|
|
52
|
-
set left(v) {
|
|
53
|
-
if (v) {
|
|
54
|
-
v.parent = this;
|
|
55
|
-
}
|
|
56
|
-
this._left = v;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Gets the right child of the node.
|
|
60
|
-
* @remarks Time O(1), Space O(1)
|
|
61
|
-
*
|
|
62
|
-
* @returns The right child.
|
|
63
|
-
*/
|
|
64
|
-
get right() {
|
|
65
|
-
return this._right;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Sets the right child of the node and updates its parent reference.
|
|
69
|
-
* @remarks Time O(1), Space O(1)
|
|
70
|
-
*
|
|
71
|
-
* @param v - The node to set as the right child.
|
|
72
|
-
*/
|
|
73
|
-
set right(v) {
|
|
74
|
-
if (v) {
|
|
75
|
-
v.parent = this;
|
|
76
|
-
}
|
|
77
|
-
this._right = v;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Gets the height of the node (used in self-balancing trees).
|
|
81
|
-
* @remarks Time O(1), Space O(1)
|
|
82
|
-
*
|
|
83
|
-
* @returns The height.
|
|
84
|
-
*/
|
|
85
|
-
get height() {
|
|
86
|
-
return this._height;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Sets the height of the node.
|
|
90
|
-
* @remarks Time O(1), Space O(1)
|
|
91
|
-
*
|
|
92
|
-
* @param value - The new height.
|
|
93
|
-
*/
|
|
94
|
-
set height(value) {
|
|
95
|
-
this._height = value;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Gets the color of the node (used in Red-Black trees).
|
|
99
|
-
* @remarks Time O(1), Space O(1)
|
|
100
|
-
*
|
|
101
|
-
* @returns The node's color.
|
|
102
|
-
*/
|
|
103
|
-
get color() {
|
|
104
|
-
return this._color;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Sets the color of the node.
|
|
108
|
-
* @remarks Time O(1), Space O(1)
|
|
109
|
-
*
|
|
110
|
-
* @param value - The new color.
|
|
111
|
-
*/
|
|
112
|
-
set color(value) {
|
|
113
|
-
this._color = value;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Gets the count of nodes in the subtree rooted at this node (used in order-statistic trees).
|
|
117
|
-
* @remarks Time O(1), Space O(1)
|
|
118
|
-
*
|
|
119
|
-
* @returns The subtree node count.
|
|
120
|
-
*/
|
|
121
|
-
get count() {
|
|
122
|
-
return this._count;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Sets the count of nodes in the subtree.
|
|
126
|
-
* @remarks Time O(1), Space O(1)
|
|
127
|
-
*
|
|
128
|
-
* @param value - The new count.
|
|
129
|
-
*/
|
|
130
|
-
set count(value) {
|
|
131
|
-
this._count = value;
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Gets the position of the node relative to its parent.
|
|
135
|
-
* @remarks Time O(1), Space O(1)
|
|
136
|
-
*
|
|
137
|
-
* @returns The family position (e.g., 'ROOT', 'LEFT', 'RIGHT').
|
|
138
|
-
*/
|
|
139
|
-
get familyPosition() {
|
|
140
|
-
if (!this.parent) {
|
|
141
|
-
return this.left || this.right ? 'ROOT' : 'ISOLATED';
|
|
142
|
-
}
|
|
143
|
-
if (this.parent.left === this) {
|
|
144
|
-
return this.left || this.right ? 'ROOT_LEFT' : 'LEFT';
|
|
145
|
-
}
|
|
146
|
-
else if (this.parent.right === this) {
|
|
147
|
-
return this.left || this.right ? 'ROOT_RIGHT' : 'RIGHT';
|
|
148
|
-
}
|
|
149
|
-
return 'MAL_NODE';
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
exports.BinaryTreeNode = BinaryTreeNode;
|
|
153
|
-
/**
|
|
154
|
-
* A general Binary Tree implementation.
|
|
155
|
-
*
|
|
156
|
-
* @remarks
|
|
157
|
-
* This class implements a basic Binary Tree, not a Binary Search Tree.
|
|
158
|
-
* The `add` operation inserts nodes level-by-level (BFS) into the first available slot.
|
|
159
|
-
*
|
|
160
|
-
* @template K - The type of the key.
|
|
161
|
-
* @template V - The type of the value.
|
|
162
|
-
* @template R - The type of the raw data object (if using `toEntryFn`).
|
|
163
|
-
* 1. Two Children Maximum: Each node has at most two children.
|
|
164
|
-
* 2. Left and Right Children: Nodes have distinct left and right children.
|
|
165
|
-
* 3. Depth and Height: Depth is the number of edges from the root to a node; height is the maximum depth in the tree.
|
|
166
|
-
* 4. Subtrees: Each child of a node forms the root of a subtree.
|
|
167
|
-
* 5. Leaf Nodes: Nodes without children are leaves.
|
|
168
|
-
* @example
|
|
169
|
-
* // determine loan approval using a decision tree
|
|
170
|
-
* // Decision tree structure
|
|
171
|
-
* const loanDecisionTree = new BinaryTree<string>(
|
|
172
|
-
* ['stableIncome', 'goodCredit', 'Rejected', 'Approved', 'Rejected'],
|
|
173
|
-
* { isDuplicate: true }
|
|
174
|
-
* );
|
|
175
|
-
*
|
|
176
|
-
* function determineLoanApproval(
|
|
177
|
-
* node?: BinaryTreeNode<string> | null,
|
|
178
|
-
* conditions?: { [key: string]: boolean }
|
|
179
|
-
* ): string {
|
|
180
|
-
* if (!node) throw new Error('Invalid node');
|
|
181
|
-
*
|
|
182
|
-
* // If it's a leaf node, return the decision result
|
|
183
|
-
* if (!node.left && !node.right) return node.key;
|
|
184
|
-
*
|
|
185
|
-
* // Check if a valid condition exists for the current node's key
|
|
186
|
-
* return conditions?.[node.key]
|
|
187
|
-
* ? determineLoanApproval(node.left, conditions)
|
|
188
|
-
* : determineLoanApproval(node.right, conditions);
|
|
189
|
-
* }
|
|
190
|
-
*
|
|
191
|
-
* // Test case 1: Stable income and good credit score
|
|
192
|
-
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: true, goodCredit: true })); // 'Approved'
|
|
193
|
-
*
|
|
194
|
-
* // Test case 2: Stable income but poor credit score
|
|
195
|
-
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: true, goodCredit: false })); // 'Rejected'
|
|
196
|
-
*
|
|
197
|
-
* // Test case 3: No stable income
|
|
198
|
-
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: false, goodCredit: true })); // 'Rejected'
|
|
199
|
-
*
|
|
200
|
-
* // Test case 4: No stable income and poor credit score
|
|
201
|
-
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: false, goodCredit: false })); // 'Rejected'
|
|
202
|
-
* @example
|
|
203
|
-
* // evaluate the arithmetic expression represented by the binary tree
|
|
204
|
-
* const expressionTree = new BinaryTree<number | string>(['+', 3, '*', null, null, 5, '-', null, null, 2, 8]);
|
|
205
|
-
*
|
|
206
|
-
* function evaluate(node?: BinaryTreeNode<number | string> | null): number {
|
|
207
|
-
* if (!node) return 0;
|
|
208
|
-
*
|
|
209
|
-
* if (typeof node.key === 'number') return node.key;
|
|
210
|
-
*
|
|
211
|
-
* const leftValue = evaluate(node.left); // Evaluate the left subtree
|
|
212
|
-
* const rightValue = evaluate(node.right); // Evaluate the right subtree
|
|
213
|
-
*
|
|
214
|
-
* // Perform the operation based on the current node's operator
|
|
215
|
-
* switch (node.key) {
|
|
216
|
-
* case '+':
|
|
217
|
-
* return leftValue + rightValue;
|
|
218
|
-
* case '-':
|
|
219
|
-
* return leftValue - rightValue;
|
|
220
|
-
* case '*':
|
|
221
|
-
* return leftValue * rightValue;
|
|
222
|
-
* case '/':
|
|
223
|
-
* return rightValue !== 0 ? leftValue / rightValue : 0; // Handle division by zero
|
|
224
|
-
* default:
|
|
225
|
-
* throw new Error(`Unsupported operator: ${node.key}`);
|
|
226
|
-
* }
|
|
227
|
-
* }
|
|
228
|
-
*
|
|
229
|
-
* console.log(evaluate(expressionTree.root)); // -27
|
|
230
|
-
*/
|
|
231
|
-
class BinaryTree extends base_1.IterableEntryBase {
|
|
232
|
-
/**
|
|
233
|
-
* Creates an instance of BinaryTree.
|
|
234
|
-
* @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.
|
|
235
|
-
*
|
|
236
|
-
* @param [keysNodesEntriesOrRaws=[]] - An iterable of items to add.
|
|
237
|
-
* @param [options] - Configuration options for the tree.
|
|
238
|
-
*/
|
|
239
|
-
constructor(keysNodesEntriesOrRaws = [], options) {
|
|
240
|
-
super();
|
|
241
|
-
this.iterationType = 'ITERATIVE';
|
|
242
|
-
this._isMapMode = true;
|
|
243
|
-
this._isDuplicate = false;
|
|
244
|
-
this._store = new Map();
|
|
245
|
-
this._size = 0;
|
|
246
|
-
this._NIL = new BinaryTreeNode(NaN);
|
|
247
|
-
/**
|
|
248
|
-
* (Protected) Default callback function, returns the node's key.
|
|
249
|
-
* @remarks Time O(1)
|
|
250
|
-
*
|
|
251
|
-
* @param node - The node.
|
|
252
|
-
* @returns The node's key or undefined.
|
|
253
|
-
*/
|
|
254
|
-
this._DEFAULT_NODE_CALLBACK = (node) => (node ? node.key : undefined);
|
|
255
|
-
if (options) {
|
|
256
|
-
const { iterationType, toEntryFn, isMapMode, isDuplicate } = options;
|
|
257
|
-
if (iterationType)
|
|
258
|
-
this.iterationType = iterationType;
|
|
259
|
-
if (isMapMode !== undefined)
|
|
260
|
-
this._isMapMode = isMapMode;
|
|
261
|
-
if (isDuplicate !== undefined)
|
|
262
|
-
this._isDuplicate = isDuplicate;
|
|
263
|
-
if (typeof toEntryFn === 'function')
|
|
264
|
-
this._toEntryFn = toEntryFn;
|
|
265
|
-
else if (toEntryFn)
|
|
266
|
-
throw TypeError('toEntryFn must be a function type');
|
|
267
|
-
}
|
|
268
|
-
if (keysNodesEntriesOrRaws)
|
|
269
|
-
this.addMany(keysNodesEntriesOrRaws);
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Gets whether the tree is in Map mode.
|
|
273
|
-
* @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)
|
|
274
|
-
*
|
|
275
|
-
* @returns True if in Map mode, false otherwise.
|
|
276
|
-
*/
|
|
277
|
-
get isMapMode() {
|
|
278
|
-
return this._isMapMode;
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Gets whether the tree allows duplicate keys.
|
|
282
|
-
* @remarks Time O(1)
|
|
283
|
-
*
|
|
284
|
-
* @returns True if duplicates are allowed, false otherwise.
|
|
285
|
-
*/
|
|
286
|
-
get isDuplicate() {
|
|
287
|
-
return this._isDuplicate;
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Gets the external value store (used in Map mode).
|
|
291
|
-
* @remarks Time O(1)
|
|
292
|
-
*
|
|
293
|
-
* @returns The map storing key-value pairs.
|
|
294
|
-
*/
|
|
295
|
-
get store() {
|
|
296
|
-
return this._store;
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Gets the root node of the tree.
|
|
300
|
-
* @remarks Time O(1)
|
|
301
|
-
*
|
|
302
|
-
* @returns The root node.
|
|
303
|
-
*/
|
|
304
|
-
get root() {
|
|
305
|
-
return this._root;
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Gets the number of nodes in the tree.
|
|
309
|
-
* @remarks Time O(1)
|
|
310
|
-
*
|
|
311
|
-
* @returns The size of the tree.
|
|
312
|
-
*/
|
|
313
|
-
get size() {
|
|
314
|
-
return this._size;
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Gets the sentinel NIL node (used in self-balancing trees like Red-Black Tree).
|
|
318
|
-
* @remarks Time O(1)
|
|
319
|
-
*
|
|
320
|
-
* @returns The NIL node.
|
|
321
|
-
*/
|
|
322
|
-
get NIL() {
|
|
323
|
-
return this._NIL;
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Gets the function used to convert raw data objects (R) into [key, value] entries.
|
|
327
|
-
* @remarks Time O(1)
|
|
328
|
-
*
|
|
329
|
-
* @returns The conversion function.
|
|
330
|
-
*/
|
|
331
|
-
get toEntryFn() {
|
|
332
|
-
return this._toEntryFn;
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* (Protected) Creates a new node.
|
|
336
|
-
* @remarks Time O(1), Space O(1)
|
|
337
|
-
*
|
|
338
|
-
* @param key - The key for the new node.
|
|
339
|
-
* @param [value] - The value for the new node (used if not in Map mode).
|
|
340
|
-
* @returns The newly created node.
|
|
341
|
-
*/
|
|
342
|
-
_createNode(key, value) {
|
|
343
|
-
return new BinaryTreeNode(key, this._isMapMode ? undefined : value);
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Creates a new, empty tree of the same type and configuration.
|
|
347
|
-
* @remarks Time O(1) (excluding options cloning), Space O(1)
|
|
348
|
-
*
|
|
349
|
-
* @param [options] - Optional overrides for the new tree's options.
|
|
350
|
-
* @returns A new, empty tree instance.
|
|
351
|
-
*/
|
|
352
|
-
createTree(options) {
|
|
353
|
-
return this._createInstance(options);
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Ensures the input is a node. If it's a key or entry, it searches for the node.
|
|
357
|
-
* @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).
|
|
358
|
-
*
|
|
359
|
-
* @param keyNodeOrEntry - The item to resolve to a node.
|
|
360
|
-
* @param [iterationType=this.iterationType] - The traversal method to use if searching.
|
|
361
|
-
* @returns The resolved node, or null/undefined if not found or input is null/undefined.
|
|
362
|
-
*/
|
|
363
|
-
ensureNode(keyNodeOrEntry, iterationType = this.iterationType) {
|
|
364
|
-
if (keyNodeOrEntry === null)
|
|
365
|
-
return null;
|
|
366
|
-
if (keyNodeOrEntry === undefined)
|
|
367
|
-
return;
|
|
368
|
-
if (keyNodeOrEntry === this._NIL)
|
|
369
|
-
return;
|
|
370
|
-
if (this.isNode(keyNodeOrEntry))
|
|
371
|
-
return keyNodeOrEntry;
|
|
372
|
-
if (this.isEntry(keyNodeOrEntry)) {
|
|
373
|
-
const key = keyNodeOrEntry[0];
|
|
374
|
-
if (key === null)
|
|
375
|
-
return null;
|
|
376
|
-
if (key === undefined)
|
|
377
|
-
return;
|
|
378
|
-
return this.getNode(key, this._root, iterationType);
|
|
379
|
-
}
|
|
380
|
-
return this.getNode(keyNodeOrEntry, this._root, iterationType);
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* Checks if the given item is a `BinaryTreeNode` instance.
|
|
384
|
-
* @remarks Time O(1), Space O(1)
|
|
385
|
-
*
|
|
386
|
-
* @param keyNodeOrEntry - The item to check.
|
|
387
|
-
* @returns True if it's a node, false otherwise.
|
|
388
|
-
*/
|
|
389
|
-
isNode(keyNodeOrEntry) {
|
|
390
|
-
return keyNodeOrEntry instanceof BinaryTreeNode;
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Checks if the given item is a raw data object (R) that needs conversion via `toEntryFn`.
|
|
394
|
-
* @remarks Time O(1), Space O(1)
|
|
395
|
-
*
|
|
396
|
-
* @param keyNodeEntryOrRaw - The item to check.
|
|
397
|
-
* @returns True if it's a raw object, false otherwise.
|
|
398
|
-
*/
|
|
399
|
-
isRaw(keyNodeEntryOrRaw) {
|
|
400
|
-
return this._toEntryFn !== undefined && typeof keyNodeEntryOrRaw === 'object';
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Checks if the given item is a "real" node (i.e., not null, undefined, or NIL).
|
|
404
|
-
* @remarks Time O(1), Space O(1)
|
|
405
|
-
*
|
|
406
|
-
* @param keyNodeOrEntry - The item to check.
|
|
407
|
-
* @returns True if it's a real node, false otherwise.
|
|
408
|
-
*/
|
|
409
|
-
isRealNode(keyNodeOrEntry) {
|
|
410
|
-
if (keyNodeOrEntry === this._NIL || keyNodeOrEntry === null || keyNodeOrEntry === undefined)
|
|
411
|
-
return false;
|
|
412
|
-
return this.isNode(keyNodeOrEntry);
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* Checks if the given item is either a "real" node or null.
|
|
416
|
-
* @remarks Time O(1), Space O(1)
|
|
417
|
-
*
|
|
418
|
-
* @param keyNodeOrEntry - The item to check.
|
|
419
|
-
* @returns True if it's a real node or null, false otherwise.
|
|
420
|
-
*/
|
|
421
|
-
isRealNodeOrNull(keyNodeOrEntry) {
|
|
422
|
-
return keyNodeOrEntry === null || this.isRealNode(keyNodeOrEntry);
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* Checks if the given item is the sentinel NIL node.
|
|
426
|
-
* @remarks Time O(1), Space O(1)
|
|
427
|
-
*
|
|
428
|
-
* @param keyNodeOrEntry - The item to check.
|
|
429
|
-
* @returns True if it's the NIL node, false otherwise.
|
|
430
|
-
*/
|
|
431
|
-
isNIL(keyNodeOrEntry) {
|
|
432
|
-
return keyNodeOrEntry === this._NIL;
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Checks if the given item is a `Range` object.
|
|
436
|
-
* @remarks Time O(1), Space O(1)
|
|
437
|
-
*
|
|
438
|
-
* @param keyNodeEntryOrPredicate - The item to check.
|
|
439
|
-
* @returns True if it's a Range, false otherwise.
|
|
440
|
-
*/
|
|
441
|
-
isRange(keyNodeEntryOrPredicate) {
|
|
442
|
-
return keyNodeEntryOrPredicate instanceof common_1.Range;
|
|
443
|
-
}
|
|
444
|
-
/**
|
|
445
|
-
* Checks if a node is a leaf (has no real children).
|
|
446
|
-
* @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`).
|
|
447
|
-
*
|
|
448
|
-
* @param keyNodeOrEntry - The node to check.
|
|
449
|
-
* @returns True if the node is a leaf, false otherwise.
|
|
450
|
-
*/
|
|
451
|
-
isLeaf(keyNodeOrEntry) {
|
|
452
|
-
keyNodeOrEntry = this.ensureNode(keyNodeOrEntry);
|
|
453
|
-
if (keyNodeOrEntry === undefined)
|
|
454
|
-
return false;
|
|
455
|
-
if (keyNodeOrEntry === null)
|
|
456
|
-
return true; // A null spot is considered a leaf
|
|
457
|
-
return !this.isRealNode(keyNodeOrEntry.left) && !this.isRealNode(keyNodeOrEntry.right);
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* Checks if the given item is a [key, value] entry pair.
|
|
461
|
-
* @remarks Time O(1), Space O(1)
|
|
462
|
-
*
|
|
463
|
-
* @param keyNodeOrEntry - The item to check.
|
|
464
|
-
* @returns True if it's an entry, false otherwise.
|
|
465
|
-
*/
|
|
466
|
-
isEntry(keyNodeOrEntry) {
|
|
467
|
-
return Array.isArray(keyNodeOrEntry) && keyNodeOrEntry.length === 2;
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Checks if the given key is valid (comparable or null).
|
|
471
|
-
* @remarks Time O(1), Space O(1)
|
|
472
|
-
*
|
|
473
|
-
* @param key - The key to validate.
|
|
474
|
-
* @returns True if the key is valid, false otherwise.
|
|
475
|
-
*/
|
|
476
|
-
isValidKey(key) {
|
|
477
|
-
if (key === null)
|
|
478
|
-
return true;
|
|
479
|
-
return (0, utils_1.isComparable)(key);
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Adds a new node to the tree.
|
|
483
|
-
* @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).
|
|
484
|
-
*
|
|
485
|
-
* @param keyNodeOrEntry - The key, node, or entry to add.
|
|
486
|
-
* @param [value] - The value, if providing just a key.
|
|
487
|
-
* @returns True if the addition was successful, false otherwise.
|
|
488
|
-
*/
|
|
489
|
-
add(keyNodeOrEntry, value) {
|
|
490
|
-
const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
|
491
|
-
if (newNode === undefined)
|
|
492
|
-
return false;
|
|
493
|
-
if (!this._root) {
|
|
494
|
-
this._setRoot(newNode);
|
|
495
|
-
if (this._isMapMode)
|
|
496
|
-
this._setValue(newNode === null || newNode === void 0 ? void 0 : newNode.key, newValue);
|
|
497
|
-
this._size = 1;
|
|
498
|
-
return true;
|
|
499
|
-
}
|
|
500
|
-
const queue = new queue_1.Queue([this._root]);
|
|
501
|
-
let potentialParent;
|
|
502
|
-
while (queue.length > 0) {
|
|
503
|
-
const cur = queue.shift();
|
|
504
|
-
if (!cur)
|
|
505
|
-
continue;
|
|
506
|
-
if (!this._isDuplicate) {
|
|
507
|
-
if (newNode !== null && cur.key === newNode.key) {
|
|
508
|
-
this._replaceNode(cur, newNode);
|
|
509
|
-
if (this._isMapMode)
|
|
510
|
-
this._setValue(cur.key, newValue);
|
|
511
|
-
return true; // Replaced existing node
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
if (potentialParent === undefined && (cur.left === undefined || cur.right === undefined)) {
|
|
515
|
-
potentialParent = cur;
|
|
516
|
-
}
|
|
517
|
-
if (cur.left !== null) {
|
|
518
|
-
if (cur.left)
|
|
519
|
-
queue.push(cur.left);
|
|
520
|
-
}
|
|
521
|
-
if (cur.right !== null) {
|
|
522
|
-
if (cur.right)
|
|
523
|
-
queue.push(cur.right);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
if (potentialParent) {
|
|
527
|
-
if (potentialParent.left === undefined) {
|
|
528
|
-
potentialParent.left = newNode;
|
|
529
|
-
}
|
|
530
|
-
else if (potentialParent.right === undefined) {
|
|
531
|
-
potentialParent.right = newNode;
|
|
532
|
-
}
|
|
533
|
-
if (this._isMapMode)
|
|
534
|
-
this._setValue(newNode === null || newNode === void 0 ? void 0 : newNode.key, newValue);
|
|
535
|
-
this._size++;
|
|
536
|
-
return true;
|
|
537
|
-
}
|
|
538
|
-
return false; // Should not happen if tree is not full?
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* Adds multiple items to the tree.
|
|
542
|
-
* @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).
|
|
543
|
-
*
|
|
544
|
-
* @param keysNodesEntriesOrRaws - An iterable of items to add.
|
|
545
|
-
* @param [values] - An optional parallel iterable of values.
|
|
546
|
-
* @returns An array of booleans indicating the success of each individual `add` operation.
|
|
547
|
-
*/
|
|
548
|
-
addMany(keysNodesEntriesOrRaws, values) {
|
|
549
|
-
const inserted = [];
|
|
550
|
-
let valuesIterator;
|
|
551
|
-
if (values) {
|
|
552
|
-
valuesIterator = values[Symbol.iterator]();
|
|
553
|
-
}
|
|
554
|
-
for (let keyNodeEntryOrRaw of keysNodesEntriesOrRaws) {
|
|
555
|
-
let value = undefined;
|
|
556
|
-
if (valuesIterator) {
|
|
557
|
-
const valueResult = valuesIterator.next();
|
|
558
|
-
if (!valueResult.done) {
|
|
559
|
-
value = valueResult.value;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
if (this.isRaw(keyNodeEntryOrRaw))
|
|
563
|
-
keyNodeEntryOrRaw = this._toEntryFn(keyNodeEntryOrRaw);
|
|
564
|
-
inserted.push(this.add(keyNodeEntryOrRaw, value));
|
|
565
|
-
}
|
|
566
|
-
return inserted;
|
|
567
|
-
}
|
|
568
|
-
/**
|
|
569
|
-
* Merges another tree into this one by adding all its nodes.
|
|
570
|
-
* @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`).
|
|
571
|
-
*
|
|
572
|
-
* @param anotherTree - The tree to merge.
|
|
573
|
-
*/
|
|
574
|
-
merge(anotherTree) {
|
|
575
|
-
this.addMany(anotherTree, []);
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* Clears the tree and refills it with new items.
|
|
579
|
-
* @remarks Time O(N) (for `clear`) + O(N * M) (for `addMany`) = O(N * M). Space O(M) (from `addMany`).
|
|
580
|
-
*
|
|
581
|
-
* @param keysNodesEntriesOrRaws - An iterable of items to add.
|
|
582
|
-
* @param [values] - An optional parallel iterable of values.
|
|
583
|
-
*/
|
|
584
|
-
refill(keysNodesEntriesOrRaws, values) {
|
|
585
|
-
this.clear();
|
|
586
|
-
this.addMany(keysNodesEntriesOrRaws, values);
|
|
587
|
-
}
|
|
588
|
-
/**
|
|
589
|
-
* Deletes a node from the tree.
|
|
590
|
-
* @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).
|
|
591
|
-
*
|
|
592
|
-
* @param keyNodeOrEntry - The node to delete.
|
|
593
|
-
* @returns An array containing deletion results (for compatibility with self-balancing trees).
|
|
594
|
-
*/
|
|
595
|
-
delete(keyNodeOrEntry) {
|
|
596
|
-
const deletedResult = [];
|
|
597
|
-
if (!this._root)
|
|
598
|
-
return deletedResult;
|
|
599
|
-
const curr = this.getNode(keyNodeOrEntry);
|
|
600
|
-
if (!curr)
|
|
601
|
-
return deletedResult;
|
|
602
|
-
const parent = curr === null || curr === void 0 ? void 0 : curr.parent;
|
|
603
|
-
let needBalanced;
|
|
604
|
-
let orgCurrent = curr;
|
|
605
|
-
if (!curr.left && !curr.right && !parent) {
|
|
606
|
-
// Deleting the root with no children
|
|
607
|
-
this._setRoot(undefined);
|
|
608
|
-
}
|
|
609
|
-
else if (curr.left) {
|
|
610
|
-
// Node has a left child (or two children)
|
|
611
|
-
// Find the rightmost node in the left subtree
|
|
612
|
-
const leftSubTreeRightMost = this.getRightMost(node => node, curr.left);
|
|
613
|
-
if (leftSubTreeRightMost) {
|
|
614
|
-
const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent;
|
|
615
|
-
// Swap properties
|
|
616
|
-
orgCurrent = this._swapProperties(curr, leftSubTreeRightMost);
|
|
617
|
-
// `orgCurrent` is now the node to be physically deleted (which was the rightmost)
|
|
618
|
-
if (parentOfLeftSubTreeMax) {
|
|
619
|
-
// Unlink the rightmost node
|
|
620
|
-
if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost)
|
|
621
|
-
parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left;
|
|
622
|
-
else
|
|
623
|
-
parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left;
|
|
624
|
-
needBalanced = parentOfLeftSubTreeMax;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
else if (parent) {
|
|
629
|
-
// Node has no left child, but has a parent
|
|
630
|
-
// Promote the right child (which could be null)
|
|
631
|
-
const { familyPosition: fp } = curr;
|
|
632
|
-
if (fp === 'LEFT' || fp === 'ROOT_LEFT') {
|
|
633
|
-
parent.left = curr.right;
|
|
634
|
-
}
|
|
635
|
-
else if (fp === 'RIGHT' || fp === 'ROOT_RIGHT') {
|
|
636
|
-
parent.right = curr.right;
|
|
637
|
-
}
|
|
638
|
-
needBalanced = parent;
|
|
639
|
-
}
|
|
640
|
-
else {
|
|
641
|
-
// Deleting the root, which has no left child
|
|
642
|
-
// Promote the right child as the new root
|
|
643
|
-
this._setRoot(curr.right);
|
|
644
|
-
curr.right = undefined;
|
|
645
|
-
}
|
|
646
|
-
this._size = this._size - 1;
|
|
647
|
-
deletedResult.push({ deleted: orgCurrent, needBalanced });
|
|
648
|
-
if (this._isMapMode && orgCurrent)
|
|
649
|
-
this._store.delete(orgCurrent.key);
|
|
650
|
-
return deletedResult;
|
|
651
|
-
}
|
|
652
|
-
/**
|
|
653
|
-
* Searches the tree for nodes matching a predicate.
|
|
654
|
-
* @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).
|
|
655
|
-
*
|
|
656
|
-
* @template C - The type of the callback function.
|
|
657
|
-
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
658
|
-
* @param [onlyOne=false] - If true, stops after finding the first match.
|
|
659
|
-
* @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on matching nodes.
|
|
660
|
-
* @param [startNode=this._root] - The node to start the search from.
|
|
661
|
-
* @param [iterationType=this.iterationType] - Whether to use 'RECURSIVE' or 'ITERATIVE' search.
|
|
662
|
-
* @returns An array of results from the callback function for each matching node.
|
|
663
|
-
*/
|
|
664
|
-
search(keyNodeEntryOrPredicate, onlyOne = false, callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
665
|
-
if (keyNodeEntryOrPredicate === undefined)
|
|
666
|
-
return [];
|
|
667
|
-
if (keyNodeEntryOrPredicate === null)
|
|
668
|
-
return [];
|
|
669
|
-
startNode = this.ensureNode(startNode);
|
|
670
|
-
if (!startNode)
|
|
671
|
-
return [];
|
|
672
|
-
const predicate = this._ensurePredicate(keyNodeEntryOrPredicate);
|
|
673
|
-
const ans = [];
|
|
674
|
-
if (iterationType === 'RECURSIVE') {
|
|
675
|
-
const dfs = (cur) => {
|
|
676
|
-
if (predicate(cur)) {
|
|
677
|
-
ans.push(callback(cur));
|
|
678
|
-
if (onlyOne)
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right))
|
|
682
|
-
return;
|
|
683
|
-
if (this.isRealNode(cur.left))
|
|
684
|
-
dfs(cur.left);
|
|
685
|
-
if (this.isRealNode(cur.right))
|
|
686
|
-
dfs(cur.right);
|
|
687
|
-
};
|
|
688
|
-
dfs(startNode);
|
|
689
|
-
}
|
|
690
|
-
else {
|
|
691
|
-
const stack = [startNode];
|
|
692
|
-
while (stack.length > 0) {
|
|
693
|
-
const cur = stack.pop();
|
|
694
|
-
if (this.isRealNode(cur)) {
|
|
695
|
-
if (predicate(cur)) {
|
|
696
|
-
ans.push(callback(cur));
|
|
697
|
-
if (onlyOne)
|
|
698
|
-
return ans;
|
|
699
|
-
}
|
|
700
|
-
if (this.isRealNode(cur.left))
|
|
701
|
-
stack.push(cur.left);
|
|
702
|
-
if (this.isRealNode(cur.right))
|
|
703
|
-
stack.push(cur.right);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
return ans;
|
|
708
|
-
}
|
|
709
|
-
getNodes(keyNodeEntryOrPredicate, onlyOne = false, startNode = this._root, iterationType = this.iterationType) {
|
|
710
|
-
return this.search(keyNodeEntryOrPredicate, onlyOne, node => node, startNode, iterationType);
|
|
711
|
-
}
|
|
712
|
-
/**
|
|
713
|
-
* Gets the first node matching a predicate.
|
|
714
|
-
* @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`).
|
|
715
|
-
*
|
|
716
|
-
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
717
|
-
* @param [startNode=this._root] - The node to start the search from.
|
|
718
|
-
* @param [iterationType=this.iterationType] - The traversal method.
|
|
719
|
-
* @returns The first matching node, or undefined if not found.
|
|
720
|
-
*/
|
|
721
|
-
getNode(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
|
|
722
|
-
return this.search(keyNodeEntryOrPredicate, true, node => node, startNode, iterationType)[0];
|
|
723
|
-
}
|
|
724
|
-
/**
|
|
725
|
-
* Gets the value associated with a key.
|
|
726
|
-
* @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.
|
|
727
|
-
*
|
|
728
|
-
* @param keyNodeEntryOrPredicate - The key, node, or entry to get the value for.
|
|
729
|
-
* @param [startNode=this._root] - The node to start searching from (if not in Map mode).
|
|
730
|
-
* @param [iterationType=this.iterationType] - The traversal method (if not in Map mode).
|
|
731
|
-
* @returns The associated value, or undefined.
|
|
732
|
-
*/
|
|
733
|
-
get(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
|
|
734
|
-
var _a;
|
|
735
|
-
if (this._isMapMode) {
|
|
736
|
-
const key = this._extractKey(keyNodeEntryOrPredicate);
|
|
737
|
-
if (key === null || key === undefined)
|
|
738
|
-
return;
|
|
739
|
-
return this._store.get(key);
|
|
740
|
-
}
|
|
741
|
-
return (_a = this.getNode(keyNodeEntryOrPredicate, startNode, iterationType)) === null || _a === void 0 ? void 0 : _a.value;
|
|
742
|
-
}
|
|
743
|
-
has(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
|
|
744
|
-
return this.search(keyNodeEntryOrPredicate, true, node => node, startNode, iterationType).length > 0;
|
|
745
|
-
}
|
|
746
|
-
/**
|
|
747
|
-
* Clears the tree of all nodes and values.
|
|
748
|
-
* @remarks Time O(N) if in Map mode (due to `_store.clear()`), O(1) otherwise. Space O(1)
|
|
749
|
-
*/
|
|
750
|
-
clear() {
|
|
751
|
-
this._clearNodes();
|
|
752
|
-
if (this._isMapMode)
|
|
753
|
-
this._clearValues();
|
|
754
|
-
}
|
|
755
|
-
/**
|
|
756
|
-
* Checks if the tree is empty.
|
|
757
|
-
* @remarks Time O(1), Space O(1)
|
|
758
|
-
*
|
|
759
|
-
* @returns True if the tree has no nodes, false otherwise.
|
|
760
|
-
*/
|
|
761
|
-
isEmpty() {
|
|
762
|
-
return this._size === 0;
|
|
763
|
-
}
|
|
764
|
-
/**
|
|
765
|
-
* Checks if the tree is perfectly balanced.
|
|
766
|
-
* @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).
|
|
767
|
-
*
|
|
768
|
-
* @param [startNode=this._root] - The node to start checking from.
|
|
769
|
-
* @returns True if perfectly balanced, false otherwise.
|
|
770
|
-
*/
|
|
771
|
-
isPerfectlyBalanced(startNode = this._root) {
|
|
772
|
-
return this.getMinHeight(startNode) + 1 >= this.getHeight(startNode);
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Checks if the tree is a valid Binary Search Tree (BST).
|
|
776
|
-
* @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).
|
|
777
|
-
*
|
|
778
|
-
* @param [startNode=this._root] - The node to start checking from.
|
|
779
|
-
* @param [iterationType=this.iterationType] - The traversal method.
|
|
780
|
-
* @returns True if it's a valid BST, false otherwise.
|
|
781
|
-
*/
|
|
782
|
-
isBST(startNode = this._root, iterationType = this.iterationType) {
|
|
783
|
-
const startNodeSired = this.ensureNode(startNode);
|
|
784
|
-
if (!startNodeSired)
|
|
785
|
-
return true;
|
|
786
|
-
if (iterationType === 'RECURSIVE') {
|
|
787
|
-
const dfs = (cur, min, max) => {
|
|
788
|
-
if (!this.isRealNode(cur))
|
|
789
|
-
return true;
|
|
790
|
-
const numKey = Number(cur.key);
|
|
791
|
-
if (numKey <= min || numKey >= max)
|
|
792
|
-
return false;
|
|
793
|
-
return dfs(cur.left, min, numKey) && dfs(cur.right, numKey, max);
|
|
794
|
-
};
|
|
795
|
-
const isStandardBST = dfs(startNodeSired, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
|
|
796
|
-
const isInverseBST = dfs(startNodeSired, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER); // Check for reverse BST
|
|
797
|
-
return isStandardBST || isInverseBST;
|
|
798
|
-
}
|
|
799
|
-
else {
|
|
800
|
-
// Iterative in-order traversal check
|
|
801
|
-
const checkBST = (checkMax = false) => {
|
|
802
|
-
const stack = [];
|
|
803
|
-
let prev = checkMax ? Number.MAX_SAFE_INTEGER : Number.MIN_SAFE_INTEGER;
|
|
804
|
-
let curr = startNodeSired;
|
|
805
|
-
while (this.isRealNode(curr) || stack.length > 0) {
|
|
806
|
-
while (this.isRealNode(curr)) {
|
|
807
|
-
stack.push(curr);
|
|
808
|
-
curr = curr.left;
|
|
809
|
-
}
|
|
810
|
-
curr = stack.pop();
|
|
811
|
-
const numKey = Number(curr.key);
|
|
812
|
-
if (!this.isRealNode(curr) || (!checkMax && prev >= numKey) || (checkMax && prev <= numKey))
|
|
813
|
-
return false;
|
|
814
|
-
prev = numKey;
|
|
815
|
-
curr = curr.right;
|
|
816
|
-
}
|
|
817
|
-
return true;
|
|
818
|
-
};
|
|
819
|
-
const isStandardBST = checkBST(false);
|
|
820
|
-
const isInverseBST = checkBST(true);
|
|
821
|
-
return isStandardBST || isInverseBST;
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
/**
|
|
825
|
-
* Gets the depth of a node (distance from `startNode`).
|
|
826
|
-
* @remarks Time O(H), where H is the depth of the `dist` node relative to `startNode`. O(N) worst-case. Space O(1).
|
|
827
|
-
*
|
|
828
|
-
* @param dist - The node to find the depth of.
|
|
829
|
-
* @param [startNode=this._root] - The node to measure depth from (defaults to root).
|
|
830
|
-
* @returns The depth (0 if `dist` is `startNode`).
|
|
831
|
-
*/
|
|
832
|
-
getDepth(dist, startNode = this._root) {
|
|
833
|
-
let distEnsured = this.ensureNode(dist);
|
|
834
|
-
const beginRootEnsured = this.ensureNode(startNode);
|
|
835
|
-
let depth = 0;
|
|
836
|
-
while (distEnsured === null || distEnsured === void 0 ? void 0 : distEnsured.parent) {
|
|
837
|
-
if (distEnsured === beginRootEnsured) {
|
|
838
|
-
return depth;
|
|
839
|
-
}
|
|
840
|
-
depth++;
|
|
841
|
-
distEnsured = distEnsured.parent;
|
|
842
|
-
}
|
|
843
|
-
return depth;
|
|
844
|
-
}
|
|
845
|
-
/**
|
|
846
|
-
* Gets the maximum height of the tree (longest path from startNode to a leaf).
|
|
847
|
-
* @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).
|
|
848
|
-
*
|
|
849
|
-
* @param [startNode=this._root] - The node to start measuring from.
|
|
850
|
-
* @param [iterationType=this.iterationType] - The traversal method.
|
|
851
|
-
* @returns The height ( -1 for an empty tree, 0 for a single-node tree).
|
|
852
|
-
*/
|
|
853
|
-
getHeight(startNode = this._root, iterationType = this.iterationType) {
|
|
854
|
-
startNode = this.ensureNode(startNode);
|
|
855
|
-
if (!this.isRealNode(startNode))
|
|
856
|
-
return -1;
|
|
857
|
-
if (iterationType === 'RECURSIVE') {
|
|
858
|
-
const _getMaxHeight = (cur) => {
|
|
859
|
-
if (!this.isRealNode(cur))
|
|
860
|
-
return -1;
|
|
861
|
-
const leftHeight = _getMaxHeight(cur.left);
|
|
862
|
-
const rightHeight = _getMaxHeight(cur.right);
|
|
863
|
-
return Math.max(leftHeight, rightHeight) + 1;
|
|
864
|
-
};
|
|
865
|
-
return _getMaxHeight(startNode);
|
|
866
|
-
}
|
|
867
|
-
else {
|
|
868
|
-
// Iterative (using DFS)
|
|
869
|
-
const stack = [{ node: startNode, depth: 0 }];
|
|
870
|
-
let maxHeight = 0;
|
|
871
|
-
while (stack.length > 0) {
|
|
872
|
-
const { node, depth } = stack.pop();
|
|
873
|
-
if (this.isRealNode(node.left))
|
|
874
|
-
stack.push({ node: node.left, depth: depth + 1 });
|
|
875
|
-
if (this.isRealNode(node.right))
|
|
876
|
-
stack.push({ node: node.right, depth: depth + 1 });
|
|
877
|
-
maxHeight = Math.max(maxHeight, depth);
|
|
878
|
-
}
|
|
879
|
-
return maxHeight;
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
/**
|
|
883
|
-
* Gets the minimum height of the tree (shortest path from startNode to a leaf).
|
|
884
|
-
* @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).
|
|
885
|
-
*
|
|
886
|
-
* @param [startNode=this._root] - The node to start measuring from.
|
|
887
|
-
* @param [iterationType=this.iterationType] - The traversal method.
|
|
888
|
-
* @returns The minimum height (-1 for empty, 0 for single node).
|
|
889
|
-
*/
|
|
890
|
-
getMinHeight(startNode = this._root, iterationType = this.iterationType) {
|
|
891
|
-
startNode = this.ensureNode(startNode);
|
|
892
|
-
if (!startNode)
|
|
893
|
-
return -1;
|
|
894
|
-
if (iterationType === 'RECURSIVE') {
|
|
895
|
-
const _getMinHeight = (cur) => {
|
|
896
|
-
if (!this.isRealNode(cur))
|
|
897
|
-
return 0;
|
|
898
|
-
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right))
|
|
899
|
-
return 0; // Leaf node
|
|
900
|
-
const leftMinHeight = _getMinHeight(cur.left);
|
|
901
|
-
const rightMinHeight = _getMinHeight(cur.right);
|
|
902
|
-
return Math.min(leftMinHeight, rightMinHeight) + 1;
|
|
903
|
-
};
|
|
904
|
-
return _getMinHeight(startNode);
|
|
905
|
-
}
|
|
906
|
-
else {
|
|
907
|
-
// Iterative (using post-order DFS)
|
|
908
|
-
const stack = [];
|
|
909
|
-
let node = startNode, last = null;
|
|
910
|
-
const depths = new Map();
|
|
911
|
-
while (stack.length > 0 || node) {
|
|
912
|
-
if (this.isRealNode(node)) {
|
|
913
|
-
stack.push(node);
|
|
914
|
-
node = node.left;
|
|
915
|
-
}
|
|
916
|
-
else {
|
|
917
|
-
node = stack[stack.length - 1];
|
|
918
|
-
if (!this.isRealNode(node.right) || last === node.right) {
|
|
919
|
-
node = stack.pop();
|
|
920
|
-
if (this.isRealNode(node)) {
|
|
921
|
-
const leftMinHeight = this.isRealNode(node.left) ? depths.get(node.left) : -1;
|
|
922
|
-
const rightMinHeight = this.isRealNode(node.right) ? depths.get(node.right) : -1;
|
|
923
|
-
depths.set(node, 1 + Math.min(leftMinHeight, rightMinHeight));
|
|
924
|
-
last = node;
|
|
925
|
-
node = null;
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
else
|
|
929
|
-
node = node.right;
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
return depths.get(startNode);
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
/**
|
|
936
|
-
* Gets the path from a given node up to the root.
|
|
937
|
-
* @remarks Time O(H), where H is the depth of the `beginNode`. O(N) worst-case. Space O(H) for the result array.
|
|
938
|
-
*
|
|
939
|
-
* @template C - The type of the callback function.
|
|
940
|
-
* @param beginNode - The node to start the path from.
|
|
941
|
-
* @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on each node in the path.
|
|
942
|
-
* @param [isReverse=false] - If true, returns the path from root-to-node.
|
|
943
|
-
* @returns An array of callback results.
|
|
944
|
-
*/
|
|
945
|
-
getPathToRoot(beginNode, callback = this._DEFAULT_NODE_CALLBACK, isReverse = false) {
|
|
946
|
-
const result = [];
|
|
947
|
-
let beginNodeEnsured = this.ensureNode(beginNode);
|
|
948
|
-
if (!beginNodeEnsured)
|
|
949
|
-
return result;
|
|
950
|
-
while (beginNodeEnsured.parent) {
|
|
951
|
-
result.push(callback(beginNodeEnsured));
|
|
952
|
-
beginNodeEnsured = beginNodeEnsured.parent;
|
|
953
|
-
}
|
|
954
|
-
result.push(callback(beginNodeEnsured)); // Add the root
|
|
955
|
-
return isReverse ? result.reverse() : result;
|
|
956
|
-
}
|
|
957
|
-
/**
|
|
958
|
-
* Finds the leftmost node in a subtree (the node with the smallest key in a BST).
|
|
959
|
-
* @remarks Time O(H), where H is the height of the left spine. O(N) worst-case. Space O(H) for recursive/trampoline stack.
|
|
960
|
-
*
|
|
961
|
-
* @template C - The type of the callback function.
|
|
962
|
-
* @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on the leftmost node.
|
|
963
|
-
* @param [startNode=this._root] - The subtree root to search from.
|
|
964
|
-
* @param [iterationType=this.iterationType] - The traversal method.
|
|
965
|
-
* @returns The callback result for the leftmost node.
|
|
966
|
-
*/
|
|
967
|
-
getLeftMost(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
968
|
-
if (this.isNIL(startNode))
|
|
969
|
-
return callback(undefined);
|
|
970
|
-
const ensuredStartNode = this.ensureNode(startNode);
|
|
971
|
-
if (!this.isRealNode(ensuredStartNode))
|
|
972
|
-
return callback(undefined);
|
|
973
|
-
if (iterationType === 'RECURSIVE') {
|
|
974
|
-
const dfs = (cur) => {
|
|
975
|
-
const { left } = cur;
|
|
976
|
-
if (!this.isRealNode(left))
|
|
977
|
-
return cur;
|
|
978
|
-
return dfs(left);
|
|
979
|
-
};
|
|
980
|
-
return callback(dfs(ensuredStartNode));
|
|
981
|
-
}
|
|
982
|
-
else {
|
|
983
|
-
// Iterative (trampolined to prevent stack overflow, though 'ITERATIVE' usually means a loop)
|
|
984
|
-
const dfs = (0, utils_1.makeTrampoline)((cur) => {
|
|
985
|
-
const { left } = cur;
|
|
986
|
-
if (!this.isRealNode(left))
|
|
987
|
-
return cur;
|
|
988
|
-
return (0, utils_1.makeTrampolineThunk)(() => dfs(left));
|
|
989
|
-
});
|
|
990
|
-
return callback(dfs(ensuredStartNode));
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
/**
|
|
994
|
-
* Finds the rightmost node in a subtree (the node with the largest key in a BST).
|
|
995
|
-
* @remarks Time O(H), where H is the height of the right spine. O(N) worst-case. Space O(H) for recursive/trampoline stack.
|
|
996
|
-
*
|
|
997
|
-
* @template C - The type of the callback function.
|
|
998
|
-
* @param [callback=this._DEFAULT_NODE_CALLBACK] - A function to call on the rightmost node.
|
|
999
|
-
* @param [startNode=this._root] - The subtree root to search from.
|
|
1000
|
-
* @param [iterationType=this.iterationType] - The traversal method.
|
|
1001
|
-
* @returns The callback result for the rightmost node.
|
|
1002
|
-
*/
|
|
1003
|
-
getRightMost(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
1004
|
-
if (this.isNIL(startNode))
|
|
1005
|
-
return callback(undefined);
|
|
1006
|
-
startNode = this.ensureNode(startNode);
|
|
1007
|
-
if (!startNode)
|
|
1008
|
-
return callback(undefined);
|
|
1009
|
-
if (iterationType === 'RECURSIVE') {
|
|
1010
|
-
const dfs = (cur) => {
|
|
1011
|
-
const { right } = cur;
|
|
1012
|
-
if (!this.isRealNode(right))
|
|
1013
|
-
return cur;
|
|
1014
|
-
return dfs(right);
|
|
1015
|
-
};
|
|
1016
|
-
return callback(dfs(startNode));
|
|
1017
|
-
}
|
|
1018
|
-
else {
|
|
1019
|
-
const dfs = (0, utils_1.makeTrampoline)((cur) => {
|
|
1020
|
-
const { right } = cur;
|
|
1021
|
-
if (!this.isRealNode(right))
|
|
1022
|
-
return cur;
|
|
1023
|
-
return (0, utils_1.makeTrampolineThunk)(() => dfs(right));
|
|
1024
|
-
});
|
|
1025
|
-
return callback(dfs(startNode));
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
/**
|
|
1029
|
-
* Gets the Morris traversal predecessor (rightmost node in the left subtree, or node itself).
|
|
1030
|
-
* @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).
|
|
1031
|
-
*
|
|
1032
|
-
* @param node - The node to find the predecessor for.
|
|
1033
|
-
* @returns The Morris predecessor.
|
|
1034
|
-
*/
|
|
1035
|
-
getPredecessor(node) {
|
|
1036
|
-
if (this.isRealNode(node.left)) {
|
|
1037
|
-
let predecessor = node.left;
|
|
1038
|
-
while (!this.isRealNode(predecessor) || (this.isRealNode(predecessor.right) && predecessor.right !== node)) {
|
|
1039
|
-
if (this.isRealNode(predecessor)) {
|
|
1040
|
-
predecessor = predecessor.right;
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
return predecessor;
|
|
1044
|
-
}
|
|
1045
|
-
else {
|
|
1046
|
-
return node;
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
/**
|
|
1050
|
-
* Gets the in-order successor of a node in a BST.
|
|
1051
|
-
* @remarks Time O(H), where H is the tree height. O(N) worst-case. Space O(H) (due to `getLeftMost` stack).
|
|
1052
|
-
*
|
|
1053
|
-
* @param [x] - The node to find the successor of.
|
|
1054
|
-
* @returns The successor node, or null/undefined if none exists.
|
|
1055
|
-
*/
|
|
1056
|
-
getSuccessor(x) {
|
|
1057
|
-
x = this.ensureNode(x);
|
|
1058
|
-
if (!this.isRealNode(x))
|
|
1059
|
-
return undefined;
|
|
1060
|
-
if (this.isRealNode(x.right)) {
|
|
1061
|
-
return this.getLeftMost(node => node, x.right);
|
|
1062
|
-
}
|
|
1063
|
-
let y = x.parent;
|
|
1064
|
-
while (this.isRealNode(y) && x === y.right) {
|
|
1065
|
-
x = y;
|
|
1066
|
-
y = y.parent;
|
|
1067
|
-
}
|
|
1068
|
-
return y;
|
|
1069
|
-
}
|
|
1070
|
-
/**
|
|
1071
|
-
* Performs a Depth-First Search (DFS) traversal.
|
|
1072
|
-
* @remarks Time O(N), visits every node. Space O(H) for the call/explicit stack. O(N) worst-case.
|
|
1073
|
-
*
|
|
1074
|
-
* @template C - The type of the callback function.
|
|
1075
|
-
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
|
|
1076
|
-
* @param [pattern='IN'] - The traversal order ('IN', 'PRE', 'POST').
|
|
1077
|
-
* @param [onlyOne=false] - If true, stops after the first callback.
|
|
1078
|
-
* @param [startNode=this._root] - The node to start from.
|
|
1079
|
-
* @param [iterationType=this.iterationType] - The traversal method.
|
|
1080
|
-
* @param [includeNull=false] - If true, includes null nodes in the traversal.
|
|
1081
|
-
* @returns An array of callback results.
|
|
1082
|
-
*/
|
|
1083
|
-
dfs(callback = this._DEFAULT_NODE_CALLBACK, pattern = 'IN', onlyOne = false, startNode = this._root, iterationType = this.iterationType, includeNull = false) {
|
|
1084
|
-
startNode = this.ensureNode(startNode);
|
|
1085
|
-
if (!startNode)
|
|
1086
|
-
return [];
|
|
1087
|
-
return this._dfs(callback, pattern, onlyOne, startNode, iterationType, includeNull);
|
|
1088
|
-
}
|
|
1089
|
-
/**
|
|
1090
|
-
* Performs a Breadth-First Search (BFS) or Level-Order traversal.
|
|
1091
|
-
* @remarks Time O(N), visits every node. Space O(N) in the worst case for the queue (e.g., a full last level).
|
|
1092
|
-
*
|
|
1093
|
-
* @template C - The type of the callback function.
|
|
1094
|
-
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
|
|
1095
|
-
* @param [startNode=this._root] - The node to start from.
|
|
1096
|
-
* @param [iterationType=this.iterationType] - The traversal method ('RECURSIVE' BFS is less common but supported here).
|
|
1097
|
-
* @param [includeNull=false] - If true, includes null nodes in the traversal.
|
|
1098
|
-
* @returns An array of callback results.
|
|
1099
|
-
*/
|
|
1100
|
-
bfs(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType, includeNull = false) {
|
|
1101
|
-
startNode = this.ensureNode(startNode);
|
|
1102
|
-
if (!startNode)
|
|
1103
|
-
return [];
|
|
1104
|
-
const ans = [];
|
|
1105
|
-
if (iterationType === 'RECURSIVE') {
|
|
1106
|
-
// This is a "recursive" BFS, which is atypical. It uses a queue but calls itself.
|
|
1107
|
-
const queue = new queue_1.Queue([
|
|
1108
|
-
startNode
|
|
1109
|
-
]);
|
|
1110
|
-
const dfs = (level) => {
|
|
1111
|
-
if (queue.length === 0)
|
|
1112
|
-
return;
|
|
1113
|
-
const current = queue.shift();
|
|
1114
|
-
ans.push(callback(current));
|
|
1115
|
-
if (includeNull) {
|
|
1116
|
-
if (current && this.isRealNodeOrNull(current.left))
|
|
1117
|
-
queue.push(current.left);
|
|
1118
|
-
if (current && this.isRealNodeOrNull(current.right))
|
|
1119
|
-
queue.push(current.right);
|
|
1120
|
-
}
|
|
1121
|
-
else {
|
|
1122
|
-
if (this.isRealNode(current.left))
|
|
1123
|
-
queue.push(current.left);
|
|
1124
|
-
if (this.isRealNode(current.right))
|
|
1125
|
-
queue.push(current.right);
|
|
1126
|
-
}
|
|
1127
|
-
dfs(level + 1);
|
|
1128
|
-
};
|
|
1129
|
-
dfs(0);
|
|
1130
|
-
}
|
|
1131
|
-
else {
|
|
1132
|
-
// Standard iterative BFS
|
|
1133
|
-
const queue = new queue_1.Queue([startNode]);
|
|
1134
|
-
while (queue.length > 0) {
|
|
1135
|
-
const levelSize = queue.length; // Not strictly needed here, but good for level-by-level
|
|
1136
|
-
for (let i = 0; i < levelSize; i++) {
|
|
1137
|
-
const current = queue.shift();
|
|
1138
|
-
ans.push(callback(current));
|
|
1139
|
-
if (includeNull) {
|
|
1140
|
-
if (current && this.isRealNodeOrNull(current.left))
|
|
1141
|
-
queue.push(current.left);
|
|
1142
|
-
if (current && this.isRealNodeOrNull(current.right))
|
|
1143
|
-
queue.push(current.right);
|
|
1144
|
-
}
|
|
1145
|
-
else {
|
|
1146
|
-
if (this.isRealNode(current.left))
|
|
1147
|
-
queue.push(current.left);
|
|
1148
|
-
if (this.isRealNode(current.right))
|
|
1149
|
-
queue.push(current.right);
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
return ans;
|
|
1155
|
-
}
|
|
1156
|
-
/**
|
|
1157
|
-
* Finds all leaf nodes in the tree.
|
|
1158
|
-
* @remarks Time O(N), visits every node. Space O(H) for recursive stack or O(N) for iterative queue.
|
|
1159
|
-
*
|
|
1160
|
-
* @template C - The type of the callback function.
|
|
1161
|
-
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each leaf node.
|
|
1162
|
-
* @param [startNode=this._root] - The node to start from.
|
|
1163
|
-
* @param [iterationType=this.iterationType] - The traversal method.
|
|
1164
|
-
* @returns An array of callback results.
|
|
1165
|
-
*/
|
|
1166
|
-
leaves(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType) {
|
|
1167
|
-
startNode = this.ensureNode(startNode);
|
|
1168
|
-
const leaves = [];
|
|
1169
|
-
if (!this.isRealNode(startNode))
|
|
1170
|
-
return [];
|
|
1171
|
-
if (iterationType === 'RECURSIVE') {
|
|
1172
|
-
// DFS-based
|
|
1173
|
-
const dfs = (cur) => {
|
|
1174
|
-
if (this.isLeaf(cur)) {
|
|
1175
|
-
leaves.push(callback(cur));
|
|
1176
|
-
}
|
|
1177
|
-
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right))
|
|
1178
|
-
return;
|
|
1179
|
-
if (this.isRealNode(cur.left))
|
|
1180
|
-
dfs(cur.left);
|
|
1181
|
-
if (this.isRealNode(cur.right))
|
|
1182
|
-
dfs(cur.right);
|
|
1183
|
-
};
|
|
1184
|
-
dfs(startNode);
|
|
1185
|
-
}
|
|
1186
|
-
else {
|
|
1187
|
-
// BFS-based
|
|
1188
|
-
const queue = new queue_1.Queue([startNode]);
|
|
1189
|
-
while (queue.length > 0) {
|
|
1190
|
-
const cur = queue.shift();
|
|
1191
|
-
if (this.isRealNode(cur)) {
|
|
1192
|
-
if (this.isLeaf(cur)) {
|
|
1193
|
-
leaves.push(callback(cur));
|
|
1194
|
-
}
|
|
1195
|
-
if (this.isRealNode(cur.left))
|
|
1196
|
-
queue.push(cur.left);
|
|
1197
|
-
if (this.isRealNode(cur.right))
|
|
1198
|
-
queue.push(cur.right);
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
return leaves;
|
|
1203
|
-
}
|
|
1204
|
-
/**
|
|
1205
|
-
* Returns a 2D array of nodes, grouped by level.
|
|
1206
|
-
* @remarks Time O(N), visits every node. Space O(N) for the result array and the queue/stack.
|
|
1207
|
-
*
|
|
1208
|
-
* @template C - The type of the callback function.
|
|
1209
|
-
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
|
|
1210
|
-
* @param [startNode=this._root] - The node to start from.
|
|
1211
|
-
* @param [iterationType=this.iterationType] - The traversal method.
|
|
1212
|
-
* @param [includeNull=false] - If true, includes null nodes.
|
|
1213
|
-
* @returns A 2D array of callback results.
|
|
1214
|
-
*/
|
|
1215
|
-
listLevels(callback = this._DEFAULT_NODE_CALLBACK, startNode = this._root, iterationType = this.iterationType, includeNull = false) {
|
|
1216
|
-
startNode = this.ensureNode(startNode);
|
|
1217
|
-
const levelsNodes = [];
|
|
1218
|
-
if (!startNode)
|
|
1219
|
-
return levelsNodes;
|
|
1220
|
-
if (iterationType === 'RECURSIVE') {
|
|
1221
|
-
// Pre-order DFS based level listing
|
|
1222
|
-
const _recursive = (node, level) => {
|
|
1223
|
-
if (!levelsNodes[level])
|
|
1224
|
-
levelsNodes[level] = [];
|
|
1225
|
-
levelsNodes[level].push(callback(node));
|
|
1226
|
-
if (includeNull) {
|
|
1227
|
-
if (node && this.isRealNodeOrNull(node.left))
|
|
1228
|
-
_recursive(node.left, level + 1);
|
|
1229
|
-
if (node && this.isRealNodeOrNull(node.right))
|
|
1230
|
-
_recursive(node.right, level + 1);
|
|
1231
|
-
}
|
|
1232
|
-
else {
|
|
1233
|
-
if (node && node.left)
|
|
1234
|
-
_recursive(node.left, level + 1);
|
|
1235
|
-
if (node && node.right)
|
|
1236
|
-
_recursive(node.right, level + 1);
|
|
1237
|
-
}
|
|
1238
|
-
};
|
|
1239
|
-
_recursive(startNode, 0);
|
|
1240
|
-
}
|
|
1241
|
-
else {
|
|
1242
|
-
// Iterative DFS based level listing
|
|
1243
|
-
const stack = [[startNode, 0]];
|
|
1244
|
-
while (stack.length > 0) {
|
|
1245
|
-
const head = stack.pop();
|
|
1246
|
-
const [node, level] = head;
|
|
1247
|
-
if (!levelsNodes[level])
|
|
1248
|
-
levelsNodes[level] = [];
|
|
1249
|
-
levelsNodes[level].push(callback(node));
|
|
1250
|
-
if (includeNull) {
|
|
1251
|
-
if (node && this.isRealNodeOrNull(node.right))
|
|
1252
|
-
stack.push([node.right, level + 1]);
|
|
1253
|
-
if (node && this.isRealNodeOrNull(node.left))
|
|
1254
|
-
stack.push([node.left, level + 1]);
|
|
1255
|
-
}
|
|
1256
|
-
else {
|
|
1257
|
-
if (node && node.right)
|
|
1258
|
-
stack.push([node.right, level + 1]);
|
|
1259
|
-
if (node && node.left)
|
|
1260
|
-
stack.push([node.left, level + 1]);
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
return levelsNodes;
|
|
1265
|
-
}
|
|
1266
|
-
/**
|
|
1267
|
-
* Performs a Morris (threaded) traversal.
|
|
1268
|
-
* @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).
|
|
1269
|
-
*
|
|
1270
|
-
* @template C - The type of the callback function.
|
|
1271
|
-
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on each node.
|
|
1272
|
-
* @param [pattern='IN'] - The traversal order ('IN', 'PRE', 'POST').
|
|
1273
|
-
* @param [startNode=this._root] - The node to start from.
|
|
1274
|
-
* @returns An array of callback results.
|
|
1275
|
-
*/
|
|
1276
|
-
morris(callback = this._DEFAULT_NODE_CALLBACK, pattern = 'IN', startNode = this._root) {
|
|
1277
|
-
startNode = this.ensureNode(startNode);
|
|
1278
|
-
if (!startNode)
|
|
1279
|
-
return [];
|
|
1280
|
-
const ans = [];
|
|
1281
|
-
let cur = startNode;
|
|
1282
|
-
// Helper to reverse a linked list (formed by right pointers)
|
|
1283
|
-
const _reverseEdge = (node) => {
|
|
1284
|
-
let pre = null;
|
|
1285
|
-
let next = null;
|
|
1286
|
-
while (node) {
|
|
1287
|
-
next = node.right;
|
|
1288
|
-
node.right = pre;
|
|
1289
|
-
pre = node;
|
|
1290
|
-
node = next;
|
|
1291
|
-
}
|
|
1292
|
-
return pre;
|
|
1293
|
-
};
|
|
1294
|
-
// Helper to print the reversed edge (for post-order)
|
|
1295
|
-
const _printEdge = (node) => {
|
|
1296
|
-
const tail = _reverseEdge(node);
|
|
1297
|
-
let cur = tail;
|
|
1298
|
-
while (cur) {
|
|
1299
|
-
ans.push(callback(cur));
|
|
1300
|
-
cur = cur.right;
|
|
1301
|
-
}
|
|
1302
|
-
_reverseEdge(tail); // Restore the edge
|
|
1303
|
-
};
|
|
1304
|
-
switch (pattern) {
|
|
1305
|
-
case 'IN':
|
|
1306
|
-
while (cur) {
|
|
1307
|
-
if (cur.left) {
|
|
1308
|
-
const predecessor = this.getPredecessor(cur);
|
|
1309
|
-
if (!predecessor.right) {
|
|
1310
|
-
// Create thread
|
|
1311
|
-
predecessor.right = cur;
|
|
1312
|
-
cur = cur.left;
|
|
1313
|
-
continue;
|
|
1314
|
-
}
|
|
1315
|
-
else {
|
|
1316
|
-
// Remove thread
|
|
1317
|
-
predecessor.right = null;
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
ans.push(callback(cur));
|
|
1321
|
-
cur = cur.right;
|
|
1322
|
-
}
|
|
1323
|
-
break;
|
|
1324
|
-
case 'PRE':
|
|
1325
|
-
while (cur) {
|
|
1326
|
-
if (cur.left) {
|
|
1327
|
-
const predecessor = this.getPredecessor(cur);
|
|
1328
|
-
if (!predecessor.right) {
|
|
1329
|
-
// Create thread and visit
|
|
1330
|
-
predecessor.right = cur;
|
|
1331
|
-
ans.push(callback(cur));
|
|
1332
|
-
cur = cur.left;
|
|
1333
|
-
continue;
|
|
1334
|
-
}
|
|
1335
|
-
else {
|
|
1336
|
-
// Remove thread
|
|
1337
|
-
predecessor.right = null;
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
else {
|
|
1341
|
-
ans.push(callback(cur));
|
|
1342
|
-
}
|
|
1343
|
-
cur = cur.right;
|
|
1344
|
-
}
|
|
1345
|
-
break;
|
|
1346
|
-
case 'POST':
|
|
1347
|
-
while (cur) {
|
|
1348
|
-
if (cur.left) {
|
|
1349
|
-
const predecessor = this.getPredecessor(cur);
|
|
1350
|
-
if (predecessor.right === null) {
|
|
1351
|
-
// Create thread
|
|
1352
|
-
predecessor.right = cur;
|
|
1353
|
-
cur = cur.left;
|
|
1354
|
-
continue;
|
|
1355
|
-
}
|
|
1356
|
-
else {
|
|
1357
|
-
// Remove thread and print right spine of left child
|
|
1358
|
-
predecessor.right = null;
|
|
1359
|
-
_printEdge(cur.left);
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
|
-
cur = cur.right;
|
|
1363
|
-
}
|
|
1364
|
-
_printEdge(startNode); // Print the right spine of the root
|
|
1365
|
-
break;
|
|
1366
|
-
}
|
|
1367
|
-
return ans;
|
|
1368
|
-
}
|
|
1369
|
-
/**
|
|
1370
|
-
* Clones the tree.
|
|
1371
|
-
* @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.
|
|
1372
|
-
*
|
|
1373
|
-
* @returns A new, cloned instance of the tree.
|
|
1374
|
-
*/
|
|
1375
|
-
clone() {
|
|
1376
|
-
const out = this._createInstance();
|
|
1377
|
-
this._clone(out);
|
|
1378
|
-
return out;
|
|
1379
|
-
}
|
|
1380
|
-
/**
|
|
1381
|
-
* Creates a new tree containing only the entries that satisfy the predicate.
|
|
1382
|
-
* @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.
|
|
1383
|
-
*
|
|
1384
|
-
* @param predicate - A function to test each [key, value] pair.
|
|
1385
|
-
* @param [thisArg] - `this` context for the predicate.
|
|
1386
|
-
* @returns A new, filtered tree.
|
|
1387
|
-
*/
|
|
1388
|
-
filter(predicate, thisArg) {
|
|
1389
|
-
const out = this._createInstance();
|
|
1390
|
-
let i = 0;
|
|
1391
|
-
for (const [k, v] of this)
|
|
1392
|
-
if (predicate.call(thisArg, k, v, i++, this))
|
|
1393
|
-
out.add([k, v]);
|
|
1394
|
-
return out;
|
|
1395
|
-
}
|
|
1396
|
-
/**
|
|
1397
|
-
* Creates a new tree by mapping each [key, value] pair to a new entry.
|
|
1398
|
-
* @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.
|
|
1399
|
-
*
|
|
1400
|
-
* @template MK - New key type.
|
|
1401
|
-
* @template MV - New value type.
|
|
1402
|
-
* @template MR - New raw type.
|
|
1403
|
-
* @param cb - A function to map each [key, value] pair.
|
|
1404
|
-
* @param [options] - Options for the new tree.
|
|
1405
|
-
* @param [thisArg] - `this` context for the callback.
|
|
1406
|
-
* @returns A new, mapped tree.
|
|
1407
|
-
*/
|
|
1408
|
-
map(cb, options, thisArg) {
|
|
1409
|
-
const out = this._createLike([], options);
|
|
1410
|
-
let i = 0;
|
|
1411
|
-
for (const [k, v] of this)
|
|
1412
|
-
out.add(cb.call(thisArg, k, v, i++, this));
|
|
1413
|
-
return out;
|
|
1414
|
-
}
|
|
1415
|
-
/**
|
|
1416
|
-
* Generates a string representation of the tree for visualization.
|
|
1417
|
-
* @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.
|
|
1418
|
-
*
|
|
1419
|
-
* @param [startNode=this._root] - The node to start printing from.
|
|
1420
|
-
* @param [options] - Options to control the output (e.g., show nulls).
|
|
1421
|
-
* @returns The string representation of the tree.
|
|
1422
|
-
*/
|
|
1423
|
-
toVisual(startNode = this._root, options) {
|
|
1424
|
-
const opts = Object.assign({ isShowUndefined: false, isShowNull: true, isShowRedBlackNIL: false }, options);
|
|
1425
|
-
startNode = this.ensureNode(startNode);
|
|
1426
|
-
let output = '';
|
|
1427
|
-
if (!startNode)
|
|
1428
|
-
return output;
|
|
1429
|
-
if (opts.isShowUndefined)
|
|
1430
|
-
output += `U for undefined\n`;
|
|
1431
|
-
if (opts.isShowNull)
|
|
1432
|
-
output += `N for null\n`;
|
|
1433
|
-
if (opts.isShowRedBlackNIL)
|
|
1434
|
-
output += `S for Sentinel Node(NIL)\n`;
|
|
1435
|
-
const display = (root) => {
|
|
1436
|
-
const [lines] = this._displayAux(root, opts);
|
|
1437
|
-
let paragraph = '';
|
|
1438
|
-
for (const line of lines) {
|
|
1439
|
-
paragraph += line + '\n';
|
|
1440
|
-
}
|
|
1441
|
-
output += paragraph;
|
|
1442
|
-
};
|
|
1443
|
-
display(startNode);
|
|
1444
|
-
return output;
|
|
1445
|
-
}
|
|
1446
|
-
/**
|
|
1447
|
-
* Prints a visual representation of the tree to the console.
|
|
1448
|
-
* @remarks Time O(N) (via `toVisual`). Space O(N*H) or O(N^2) (via `toVisual`).
|
|
1449
|
-
*
|
|
1450
|
-
* @param [options] - Options to control the output.
|
|
1451
|
-
* @param [startNode=this._root] - The node to start printing from.
|
|
1452
|
-
*/
|
|
1453
|
-
print(options, startNode = this._root) {
|
|
1454
|
-
console.log(this.toVisual(startNode, options));
|
|
1455
|
-
}
|
|
1456
|
-
/**
|
|
1457
|
-
* (Protected) Core DFS implementation.
|
|
1458
|
-
* @remarks Time O(N), visits every node satisfying predicates. Space O(H) for call/explicit stack. O(N) worst-case.
|
|
1459
|
-
*
|
|
1460
|
-
* @template C - Callback type.
|
|
1461
|
-
* @param [callback=this._DEFAULT_NODE_CALLBACK] - Function to call on nodes.
|
|
1462
|
-
* @param [pattern='IN'] - Traversal order.
|
|
1463
|
-
* @param [onlyOne=false] - Stop after first match.
|
|
1464
|
-
* @param [startNode=this._root] - Starting node.
|
|
1465
|
-
* @param [iterationType=this.iterationType] - Traversal method.
|
|
1466
|
-
* @param [includeNull=false] - Include nulls.
|
|
1467
|
-
* @param [shouldVisitLeft] - Predicate to traverse left.
|
|
1468
|
-
* @param [shouldVisitRight] - Predicate to traverse right.
|
|
1469
|
-
* @param [shouldVisitRoot] - Predicate to visit root.
|
|
1470
|
-
* @param [shouldProcessRoot] - Predicate to process root.
|
|
1471
|
-
* @returns Array of callback results.
|
|
1472
|
-
*/
|
|
1473
|
-
_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 => {
|
|
1474
|
-
if (includeNull)
|
|
1475
|
-
return this.isRealNodeOrNull(node);
|
|
1476
|
-
return this.isRealNode(node);
|
|
1477
|
-
}, shouldProcessRoot = node => this.isRealNodeOrNull(node)) {
|
|
1478
|
-
startNode = this.ensureNode(startNode);
|
|
1479
|
-
if (!startNode)
|
|
1480
|
-
return [];
|
|
1481
|
-
const ans = [];
|
|
1482
|
-
if (iterationType === 'RECURSIVE') {
|
|
1483
|
-
const dfs = (node) => {
|
|
1484
|
-
if (!shouldVisitRoot(node))
|
|
1485
|
-
return;
|
|
1486
|
-
const visitLeft = () => {
|
|
1487
|
-
if (shouldVisitLeft(node) && (node === null || node === void 0 ? void 0 : node.left) !== undefined)
|
|
1488
|
-
dfs(node === null || node === void 0 ? void 0 : node.left);
|
|
1489
|
-
};
|
|
1490
|
-
const visitRight = () => {
|
|
1491
|
-
if (shouldVisitRight(node) && (node === null || node === void 0 ? void 0 : node.right) !== undefined)
|
|
1492
|
-
dfs(node === null || node === void 0 ? void 0 : node.right);
|
|
1493
|
-
};
|
|
1494
|
-
switch (pattern) {
|
|
1495
|
-
case 'IN':
|
|
1496
|
-
visitLeft();
|
|
1497
|
-
if (shouldProcessRoot(node)) {
|
|
1498
|
-
ans.push(callback(node));
|
|
1499
|
-
if (onlyOne)
|
|
1500
|
-
return;
|
|
1501
|
-
}
|
|
1502
|
-
visitRight();
|
|
1503
|
-
break;
|
|
1504
|
-
case 'PRE':
|
|
1505
|
-
if (shouldProcessRoot(node)) {
|
|
1506
|
-
ans.push(callback(node));
|
|
1507
|
-
if (onlyOne)
|
|
1508
|
-
return;
|
|
1509
|
-
}
|
|
1510
|
-
visitLeft();
|
|
1511
|
-
visitRight();
|
|
1512
|
-
break;
|
|
1513
|
-
case 'POST':
|
|
1514
|
-
visitLeft();
|
|
1515
|
-
visitRight();
|
|
1516
|
-
if (shouldProcessRoot(node)) {
|
|
1517
|
-
ans.push(callback(node));
|
|
1518
|
-
if (onlyOne)
|
|
1519
|
-
return;
|
|
1520
|
-
}
|
|
1521
|
-
break;
|
|
1522
|
-
}
|
|
1523
|
-
};
|
|
1524
|
-
dfs(startNode);
|
|
1525
|
-
}
|
|
1526
|
-
else {
|
|
1527
|
-
// Iterative
|
|
1528
|
-
const stack = [{ opt: common_1.DFSOperation.VISIT, node: startNode }];
|
|
1529
|
-
const pushLeft = (cur) => {
|
|
1530
|
-
var _a;
|
|
1531
|
-
if (shouldVisitLeft(cur.node))
|
|
1532
|
-
stack.push({ opt: common_1.DFSOperation.VISIT, node: (_a = cur.node) === null || _a === void 0 ? void 0 : _a.left });
|
|
1533
|
-
};
|
|
1534
|
-
const pushRight = (cur) => {
|
|
1535
|
-
var _a;
|
|
1536
|
-
if (shouldVisitRight(cur.node))
|
|
1537
|
-
stack.push({ opt: common_1.DFSOperation.VISIT, node: (_a = cur.node) === null || _a === void 0 ? void 0 : _a.right });
|
|
1538
|
-
};
|
|
1539
|
-
const pushRoot = (cur) => {
|
|
1540
|
-
if (shouldVisitRoot(cur.node))
|
|
1541
|
-
stack.push({ opt: common_1.DFSOperation.PROCESS, node: cur.node });
|
|
1542
|
-
};
|
|
1543
|
-
while (stack.length > 0) {
|
|
1544
|
-
const cur = stack.pop();
|
|
1545
|
-
if (cur === undefined)
|
|
1546
|
-
continue;
|
|
1547
|
-
if (!shouldVisitRoot(cur.node))
|
|
1548
|
-
continue;
|
|
1549
|
-
if (cur.opt === common_1.DFSOperation.PROCESS) {
|
|
1550
|
-
if (shouldProcessRoot(cur.node) && cur.node !== undefined) {
|
|
1551
|
-
ans.push(callback(cur.node));
|
|
1552
|
-
if (onlyOne)
|
|
1553
|
-
return ans;
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
else {
|
|
1557
|
-
// VISIT
|
|
1558
|
-
switch (pattern) {
|
|
1559
|
-
case 'IN':
|
|
1560
|
-
pushRight(cur);
|
|
1561
|
-
pushRoot(cur);
|
|
1562
|
-
pushLeft(cur);
|
|
1563
|
-
break;
|
|
1564
|
-
case 'PRE':
|
|
1565
|
-
pushRight(cur);
|
|
1566
|
-
pushLeft(cur);
|
|
1567
|
-
pushRoot(cur);
|
|
1568
|
-
break;
|
|
1569
|
-
case 'POST':
|
|
1570
|
-
pushRoot(cur);
|
|
1571
|
-
pushRight(cur);
|
|
1572
|
-
pushLeft(cur);
|
|
1573
|
-
break;
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
return ans;
|
|
1579
|
-
}
|
|
1580
|
-
/**
|
|
1581
|
-
* (Protected) Gets the iterator for the tree (default in-order).
|
|
1582
|
-
* @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.
|
|
1583
|
-
*
|
|
1584
|
-
* @param [node=this._root] - The node to start iteration from.
|
|
1585
|
-
* @returns An iterator for [key, value] pairs.
|
|
1586
|
-
*/
|
|
1587
|
-
*_getIterator(node = this._root) {
|
|
1588
|
-
if (!node)
|
|
1589
|
-
return;
|
|
1590
|
-
if (this.iterationType === 'ITERATIVE') {
|
|
1591
|
-
const stack = [];
|
|
1592
|
-
let current = node;
|
|
1593
|
-
while (current || stack.length > 0) {
|
|
1594
|
-
// Go to the leftmost node
|
|
1595
|
-
while (this.isRealNode(current)) {
|
|
1596
|
-
stack.push(current);
|
|
1597
|
-
current = current.left;
|
|
1598
|
-
}
|
|
1599
|
-
// Visit the node
|
|
1600
|
-
current = stack.pop();
|
|
1601
|
-
if (this.isRealNode(current)) {
|
|
1602
|
-
if (this._isMapMode)
|
|
1603
|
-
yield [current.key, this._store.get(current.key)];
|
|
1604
|
-
else
|
|
1605
|
-
yield [current.key, current.value];
|
|
1606
|
-
// Move to the right subtree
|
|
1607
|
-
current = current.right;
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
else {
|
|
1612
|
-
// Recursive in-order traversal
|
|
1613
|
-
if (node.left && this.isRealNode(node)) {
|
|
1614
|
-
yield* this[Symbol.iterator](node.left);
|
|
1615
|
-
}
|
|
1616
|
-
if (this._isMapMode)
|
|
1617
|
-
yield [node.key, this._store.get(node.key)];
|
|
1618
|
-
else
|
|
1619
|
-
yield [node.key, node.value];
|
|
1620
|
-
if (node.right && this.isRealNode(node)) {
|
|
1621
|
-
yield* this[Symbol.iterator](node.right);
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
/**
|
|
1626
|
-
* (Protected) Snapshots the current tree's configuration options.
|
|
1627
|
-
* @remarks Time O(1)
|
|
1628
|
-
*
|
|
1629
|
-
* @template TK, TV, TR - Generic types for the options.
|
|
1630
|
-
* @returns The options object.
|
|
1631
|
-
*/
|
|
1632
|
-
_snapshotOptions() {
|
|
1633
|
-
return {
|
|
1634
|
-
iterationType: this.iterationType,
|
|
1635
|
-
toEntryFn: this.toEntryFn,
|
|
1636
|
-
isMapMode: this.isMapMode,
|
|
1637
|
-
isDuplicate: this.isDuplicate
|
|
1638
|
-
};
|
|
1639
|
-
}
|
|
1640
|
-
/**
|
|
1641
|
-
* (Protected) Creates a new, empty instance of the same tree constructor.
|
|
1642
|
-
* @remarks Time O(1)
|
|
1643
|
-
*
|
|
1644
|
-
* @template TK, TV, TR - Generic types for the new instance.
|
|
1645
|
-
* @param [options] - Options for the new tree.
|
|
1646
|
-
* @returns A new, empty tree.
|
|
1647
|
-
*/
|
|
1648
|
-
_createInstance(options) {
|
|
1649
|
-
const Ctor = this.constructor;
|
|
1650
|
-
return new Ctor([], Object.assign(Object.assign({}, this._snapshotOptions()), (options !== null && options !== void 0 ? options : {})));
|
|
1651
|
-
}
|
|
1652
|
-
/**
|
|
1653
|
-
* (Protected) Creates a new instance of the same tree constructor, potentially with different generic types.
|
|
1654
|
-
* @remarks Time O(N) (or as per constructor) due to processing the iterable.
|
|
1655
|
-
*
|
|
1656
|
-
* @template TK, TV, TR - Generic types for the new instance.
|
|
1657
|
-
* @param [iter=[]] - An iterable to populate the new tree.
|
|
1658
|
-
* @param [options] - Options for the new tree.
|
|
1659
|
-
* @returns A new tree.
|
|
1660
|
-
*/
|
|
1661
|
-
_createLike(iter = [], options) {
|
|
1662
|
-
const Ctor = this.constructor;
|
|
1663
|
-
return new Ctor(iter, Object.assign(Object.assign({}, this._snapshotOptions()), (options !== null && options !== void 0 ? options : {})));
|
|
1664
|
-
}
|
|
1665
|
-
/**
|
|
1666
|
-
* (Protected) Converts a key, node, or entry into a standardized [node, value] tuple.
|
|
1667
|
-
* @remarks Time O(1)
|
|
1668
|
-
*
|
|
1669
|
-
* @param keyNodeOrEntry - The input item.
|
|
1670
|
-
* @param [value] - An optional value (used if input is just a key).
|
|
1671
|
-
* @returns A tuple of [node, value].
|
|
1672
|
-
*/
|
|
1673
|
-
_keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value) {
|
|
1674
|
-
if (keyNodeOrEntry === undefined)
|
|
1675
|
-
return [undefined, undefined];
|
|
1676
|
-
if (keyNodeOrEntry === null)
|
|
1677
|
-
return [null, undefined];
|
|
1678
|
-
if (this.isNode(keyNodeOrEntry))
|
|
1679
|
-
return [keyNodeOrEntry, value];
|
|
1680
|
-
if (this.isEntry(keyNodeOrEntry)) {
|
|
1681
|
-
const [key, entryValue] = keyNodeOrEntry;
|
|
1682
|
-
if (key === undefined)
|
|
1683
|
-
return [undefined, undefined];
|
|
1684
|
-
else if (key === null)
|
|
1685
|
-
return [null, undefined];
|
|
1686
|
-
const finalValue = value !== null && value !== void 0 ? value : entryValue;
|
|
1687
|
-
return [this._createNode(key, finalValue), finalValue];
|
|
1688
|
-
}
|
|
1689
|
-
return [this._createNode(keyNodeOrEntry, value), value];
|
|
1690
|
-
}
|
|
1691
|
-
/**
|
|
1692
|
-
* (Protected) Helper for cloning. Performs a BFS and adds all nodes to the new tree.
|
|
1693
|
-
* @remarks Time O(N * M) (O(N) BFS + O(M) `add` for each node).
|
|
1694
|
-
*
|
|
1695
|
-
* @param cloned - The new, empty tree instance to populate.
|
|
1696
|
-
*/
|
|
1697
|
-
_clone(cloned) {
|
|
1698
|
-
// Use BFS with nulls to preserve the tree structure
|
|
1699
|
-
this.bfs(node => {
|
|
1700
|
-
if (node === null)
|
|
1701
|
-
cloned.add(null);
|
|
1702
|
-
else {
|
|
1703
|
-
if (this._isMapMode)
|
|
1704
|
-
cloned.add([node.key, this._store.get(node.key)]);
|
|
1705
|
-
else
|
|
1706
|
-
cloned.add([node.key, node.value]);
|
|
1707
|
-
}
|
|
1708
|
-
}, this._root, this.iterationType, true // Include nulls
|
|
1709
|
-
);
|
|
1710
|
-
if (this._isMapMode)
|
|
1711
|
-
cloned._store = this._store;
|
|
1712
|
-
}
|
|
1713
|
-
/**
|
|
1714
|
-
* (Protected) Recursive helper for `toVisual`.
|
|
1715
|
-
* @remarks Time O(N), Space O(N*H) or O(N^2)
|
|
1716
|
-
*
|
|
1717
|
-
* @param node - The current node.
|
|
1718
|
-
* @param options - Print options.
|
|
1719
|
-
* @returns Layout information for this subtree.
|
|
1720
|
-
*/
|
|
1721
|
-
_displayAux(node, options) {
|
|
1722
|
-
const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options;
|
|
1723
|
-
const emptyDisplayLayout = [['─'], 1, 0, 0]; // Represents an empty spot
|
|
1724
|
-
if (node === null && !isShowNull) {
|
|
1725
|
-
return emptyDisplayLayout;
|
|
1726
|
-
}
|
|
1727
|
-
else if (node === undefined && !isShowUndefined) {
|
|
1728
|
-
return emptyDisplayLayout;
|
|
1729
|
-
}
|
|
1730
|
-
else if (this.isNIL(node) && !isShowRedBlackNIL) {
|
|
1731
|
-
return emptyDisplayLayout;
|
|
1732
|
-
}
|
|
1733
|
-
else if (node !== null && node !== undefined) {
|
|
1734
|
-
// Real node
|
|
1735
|
-
const key = node.key, line = this.isNIL(node) ? 'S' : String(key), width = line.length;
|
|
1736
|
-
return _buildNodeDisplay(line, width, this._displayAux(node.left, options), this._displayAux(node.right, options));
|
|
1737
|
-
}
|
|
1738
|
-
else {
|
|
1739
|
-
// Null or Undefined
|
|
1740
|
-
const line = node === undefined ? 'U' : 'N', width = line.length;
|
|
1741
|
-
// Treat as a leaf
|
|
1742
|
-
return _buildNodeDisplay(line, width, [[''], 1, 0, 0], [[''], 1, 0, 0]);
|
|
1743
|
-
}
|
|
1744
|
-
/**
|
|
1745
|
-
* (Inner) Builds the display lines for a node.
|
|
1746
|
-
* @remarks Time/Space: Proportional to the width and height of the subtrees.
|
|
1747
|
-
*/
|
|
1748
|
-
function _buildNodeDisplay(line, width, left, right) {
|
|
1749
|
-
const [leftLines, leftWidth, leftHeight, leftMiddle] = left;
|
|
1750
|
-
const [rightLines, rightWidth, rightHeight, rightMiddle] = right;
|
|
1751
|
-
const firstLine = ' '.repeat(Math.max(0, leftMiddle + 1)) +
|
|
1752
|
-
'_'.repeat(Math.max(0, leftWidth - leftMiddle - 1)) +
|
|
1753
|
-
line +
|
|
1754
|
-
'_'.repeat(Math.max(0, rightMiddle)) +
|
|
1755
|
-
' '.repeat(Math.max(0, rightWidth - rightMiddle));
|
|
1756
|
-
const secondLine = (leftHeight > 0
|
|
1757
|
-
? ' '.repeat(leftMiddle) + '/' + ' '.repeat(leftWidth - leftMiddle - 1)
|
|
1758
|
-
: ' '.repeat(leftWidth)) +
|
|
1759
|
-
' '.repeat(width) +
|
|
1760
|
-
(rightHeight > 0
|
|
1761
|
-
? ' '.repeat(rightMiddle) + '\\' + ' '.repeat(rightWidth - rightMiddle - 1)
|
|
1762
|
-
: ' '.repeat(rightWidth));
|
|
1763
|
-
const mergedLines = [firstLine, secondLine];
|
|
1764
|
-
for (let i = 0; i < Math.max(leftHeight, rightHeight); i++) {
|
|
1765
|
-
const leftLine = i < leftHeight ? leftLines[i] : ' '.repeat(leftWidth);
|
|
1766
|
-
const rightLine = i < rightHeight ? rightLines[i] : ' '.repeat(rightWidth);
|
|
1767
|
-
mergedLines.push(leftLine + ' '.repeat(width) + rightLine);
|
|
1768
|
-
}
|
|
1769
|
-
return [
|
|
1770
|
-
mergedLines,
|
|
1771
|
-
leftWidth + width + rightWidth,
|
|
1772
|
-
Math.max(leftHeight, rightHeight) + 2,
|
|
1773
|
-
leftWidth + Math.floor(width / 2)
|
|
1774
|
-
];
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
/**
|
|
1778
|
-
* (Protected) Swaps the key/value properties of two nodes.
|
|
1779
|
-
* @remarks Time O(1)
|
|
1780
|
-
*
|
|
1781
|
-
* @param srcNode - The source node.
|
|
1782
|
-
* @param destNode - The destination node.
|
|
1783
|
-
* @returns The `destNode` (now holding `srcNode`'s properties).
|
|
1784
|
-
*/
|
|
1785
|
-
_swapProperties(srcNode, destNode) {
|
|
1786
|
-
srcNode = this.ensureNode(srcNode);
|
|
1787
|
-
destNode = this.ensureNode(destNode);
|
|
1788
|
-
if (srcNode && destNode) {
|
|
1789
|
-
const { key, value } = destNode;
|
|
1790
|
-
const tempNode = this._createNode(key, value); // Use a temp node to hold dest properties
|
|
1791
|
-
if (tempNode) {
|
|
1792
|
-
// Copy src to dest
|
|
1793
|
-
destNode.key = srcNode.key;
|
|
1794
|
-
if (!this._isMapMode)
|
|
1795
|
-
destNode.value = srcNode.value;
|
|
1796
|
-
// Copy temp (original dest) to src
|
|
1797
|
-
srcNode.key = tempNode.key;
|
|
1798
|
-
if (!this._isMapMode)
|
|
1799
|
-
srcNode.value = tempNode.value;
|
|
1800
|
-
}
|
|
1801
|
-
return destNode;
|
|
1802
|
-
}
|
|
1803
|
-
return undefined;
|
|
1804
|
-
}
|
|
1805
|
-
/**
|
|
1806
|
-
* (Protected) Replaces a node in the tree with a new node, maintaining children and parent links.
|
|
1807
|
-
* @remarks Time O(1)
|
|
1808
|
-
*
|
|
1809
|
-
* @param oldNode - The node to be replaced.
|
|
1810
|
-
* @param newNode - The node to insert.
|
|
1811
|
-
* @returns The `newNode`.
|
|
1812
|
-
*/
|
|
1813
|
-
_replaceNode(oldNode, newNode) {
|
|
1814
|
-
if (oldNode.parent) {
|
|
1815
|
-
if (oldNode.parent.left === oldNode) {
|
|
1816
|
-
oldNode.parent.left = newNode;
|
|
1817
|
-
}
|
|
1818
|
-
else if (oldNode.parent.right === oldNode) {
|
|
1819
|
-
oldNode.parent.right = newNode;
|
|
1820
|
-
}
|
|
1821
|
-
}
|
|
1822
|
-
newNode.left = oldNode.left;
|
|
1823
|
-
newNode.right = oldNode.right;
|
|
1824
|
-
newNode.parent = oldNode.parent;
|
|
1825
|
-
if (this._root === oldNode) {
|
|
1826
|
-
this._setRoot(newNode);
|
|
1827
|
-
}
|
|
1828
|
-
return newNode;
|
|
1829
|
-
}
|
|
1830
|
-
/**
|
|
1831
|
-
* (Protected) Sets the root node and clears its parent reference.
|
|
1832
|
-
* @remarks Time O(1)
|
|
1833
|
-
*
|
|
1834
|
-
* @param v - The node to set as root.
|
|
1835
|
-
*/
|
|
1836
|
-
_setRoot(v) {
|
|
1837
|
-
if (v) {
|
|
1838
|
-
v.parent = undefined;
|
|
1839
|
-
}
|
|
1840
|
-
this._root = v;
|
|
1841
|
-
}
|
|
1842
|
-
_ensurePredicate(keyNodeEntryOrPredicate) {
|
|
1843
|
-
if (keyNodeEntryOrPredicate === null || keyNodeEntryOrPredicate === undefined)
|
|
1844
|
-
return (node) => (node ? false : false);
|
|
1845
|
-
if (this._isPredicate(keyNodeEntryOrPredicate))
|
|
1846
|
-
return keyNodeEntryOrPredicate;
|
|
1847
|
-
if (this.isRealNode(keyNodeEntryOrPredicate))
|
|
1848
|
-
return (node) => node === keyNodeEntryOrPredicate;
|
|
1849
|
-
if (this.isEntry(keyNodeEntryOrPredicate)) {
|
|
1850
|
-
const [key] = keyNodeEntryOrPredicate;
|
|
1851
|
-
return (node) => {
|
|
1852
|
-
if (!node)
|
|
1853
|
-
return false;
|
|
1854
|
-
return node.key === key;
|
|
1855
|
-
};
|
|
1856
|
-
}
|
|
1857
|
-
// Assume it's a key
|
|
1858
|
-
return (node) => {
|
|
1859
|
-
if (!node)
|
|
1860
|
-
return false;
|
|
1861
|
-
return node.key === keyNodeEntryOrPredicate;
|
|
1862
|
-
};
|
|
1863
|
-
}
|
|
1864
|
-
/**
|
|
1865
|
-
* (Protected) Checks if an item is a predicate function.
|
|
1866
|
-
* @remarks Time O(1)
|
|
1867
|
-
*
|
|
1868
|
-
* @param p - The item to check.
|
|
1869
|
-
* @returns True if it's a function.
|
|
1870
|
-
*/
|
|
1871
|
-
_isPredicate(p) {
|
|
1872
|
-
return typeof p === 'function';
|
|
1873
|
-
}
|
|
1874
|
-
/**
|
|
1875
|
-
* (Protected) Extracts the key from a key, node, or entry.
|
|
1876
|
-
* @remarks Time O(1)
|
|
1877
|
-
*
|
|
1878
|
-
* @param keyNodeOrEntry - The item.
|
|
1879
|
-
* @returns The extracted key.
|
|
1880
|
-
*/
|
|
1881
|
-
_extractKey(keyNodeOrEntry) {
|
|
1882
|
-
if (keyNodeOrEntry === null)
|
|
1883
|
-
return null;
|
|
1884
|
-
if (keyNodeOrEntry === undefined)
|
|
1885
|
-
return;
|
|
1886
|
-
if (keyNodeOrEntry === this._NIL)
|
|
1887
|
-
return;
|
|
1888
|
-
if (this.isNode(keyNodeOrEntry))
|
|
1889
|
-
return keyNodeOrEntry.key;
|
|
1890
|
-
if (this.isEntry(keyNodeOrEntry))
|
|
1891
|
-
return keyNodeOrEntry[0];
|
|
1892
|
-
return keyNodeOrEntry;
|
|
1893
|
-
}
|
|
1894
|
-
/**
|
|
1895
|
-
* (Protected) Sets a value in the external store (Map mode).
|
|
1896
|
-
* @remarks Time O(1) (average for Map.set).
|
|
1897
|
-
*
|
|
1898
|
-
* @param key - The key.
|
|
1899
|
-
* @param value - The value.
|
|
1900
|
-
* @returns True if successful.
|
|
1901
|
-
*/
|
|
1902
|
-
_setValue(key, value) {
|
|
1903
|
-
if (key === null || key === undefined)
|
|
1904
|
-
return false;
|
|
1905
|
-
if (value === undefined)
|
|
1906
|
-
return false; // Or allow setting undefined?
|
|
1907
|
-
return this._store.set(key, value);
|
|
1908
|
-
}
|
|
1909
|
-
/**
|
|
1910
|
-
* (Protected) Clears all nodes from the tree.
|
|
1911
|
-
* @remarks Time O(1)
|
|
1912
|
-
*/
|
|
1913
|
-
_clearNodes() {
|
|
1914
|
-
this._setRoot(undefined);
|
|
1915
|
-
this._size = 0;
|
|
1916
|
-
}
|
|
1917
|
-
/**
|
|
1918
|
-
* (Protected) Clears all values from the external store.
|
|
1919
|
-
* @remarks Time O(N)
|
|
1920
|
-
*/
|
|
1921
|
-
_clearValues() {
|
|
1922
|
-
this._store.clear();
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
1925
|
-
exports.BinaryTree = BinaryTree;
|