data-structure-typed 2.2.8 → 2.3.0
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/.github/workflows/ci.yml +9 -0
- package/CHANGELOG.md +1 -1
- package/README.md +1 -1
- package/README_CN.md +1 -1
- package/dist/cjs/index.cjs +584 -76
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +588 -79
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +584 -76
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +588 -79
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/base/linear-base.d.ts +6 -6
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +3 -4
- package/dist/types/data-structures/binary-tree/bst.d.ts +2 -1
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +150 -20
- package/dist/types/data-structures/binary-tree/tree-counter.d.ts +3 -3
- package/dist/types/interfaces/binary-tree.d.ts +1 -1
- package/dist/umd/data-structure-typed.js +588 -79
- package/dist/umd/data-structure-typed.js.map +1 -1
- package/dist/umd/data-structure-typed.min.js +3 -3
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +4 -3
- package/src/data-structures/base/linear-base.ts +2 -12
- package/src/data-structures/binary-tree/binary-tree.ts +5 -6
- package/src/data-structures/binary-tree/bst.ts +79 -4
- package/src/data-structures/binary-tree/red-black-tree.ts +583 -73
- package/src/data-structures/binary-tree/tree-counter.ts +21 -9
- package/src/data-structures/queue/deque.ts +10 -0
- package/src/interfaces/binary-tree.ts +1 -1
- package/test/unit/data-structures/base/iterable-element-base.coverage.test.ts +106 -0
- package/test/unit/data-structures/base/iterable-element-base.more-branches.coverage.test.ts +61 -0
- package/test/unit/data-structures/base/linear-base.array.coverage.test.ts +168 -0
- package/test/unit/data-structures/base/linear-base.concat-else.coverage.test.ts +82 -0
- package/test/unit/data-structures/base/linear-base.coverage.test.ts +72 -0
- package/test/unit/data-structures/base/linear-base.more-branches.coverage.test.ts +417 -0
- package/test/unit/data-structures/binary-tree/avl-tree-counter.more-branches-3.coverage.test.ts +146 -0
- package/test/unit/data-structures/binary-tree/avl-tree-counter.more-branches.coverage.test.ts +93 -0
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.coverage.test.ts +108 -0
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.more-branches-2.coverage.test.ts +85 -0
- package/test/unit/data-structures/binary-tree/avl-tree-node.familyPosition-root-left.coverage.test.ts +17 -0
- package/test/unit/data-structures/binary-tree/avl-tree.more-branches-2.coverage.test.ts +99 -0
- package/test/unit/data-structures/binary-tree/binary-indexed-tree.more-branches.coverage.test.ts +18 -0
- package/test/unit/data-structures/binary-tree/binary-tree.more-branches.coverage.test.ts +56 -0
- package/test/unit/data-structures/binary-tree/binary-tree.remaining-branches.coverage.test.ts +229 -0
- package/test/unit/data-structures/binary-tree/bst.bound-by-predicate.coverage.test.ts +33 -0
- package/test/unit/data-structures/binary-tree/bst.coverage.test.ts +94 -0
- package/test/unit/data-structures/binary-tree/bst.deletebykey.coverage.test.ts +70 -0
- package/test/unit/data-structures/binary-tree/bst.deletewhere.coverage.test.ts +37 -0
- package/test/unit/data-structures/binary-tree/bst.floor-lower-predicate.coverage.test.ts +29 -0
- package/test/unit/data-structures/binary-tree/bst.floor-setmany.coverage.test.ts +72 -0
- package/test/unit/data-structures/binary-tree/bst.getnode.range-ensure.coverage.test.ts +22 -0
- package/test/unit/data-structures/binary-tree/bst.misc-branches.coverage.test.ts +100 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-2.coverage.test.ts +133 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-3.coverage.test.ts +45 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-4.coverage.test.ts +36 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-5.coverage.test.ts +40 -0
- package/test/unit/data-structures/binary-tree/bst.more.coverage.test.ts +39 -0
- package/test/unit/data-structures/binary-tree/bst.node-family.coverage.test.ts +29 -0
- package/test/unit/data-structures/binary-tree/bst.range-pruning.coverage.test.ts +43 -0
- package/test/unit/data-structures/binary-tree/bst.search-fastpath.coverage.test.ts +30 -0
- package/test/unit/data-structures/binary-tree/bst.test.ts +25 -55
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-corruption-repair.coverage.test.ts +66 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-max-update.coverage.test.ts +18 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-null.coverage.test.ts +53 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-stale-cache.coverage.test.ts +25 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-update.coverage.test.ts +23 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.cache-delete.coverage.test.ts +49 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.cache-edge.coverage.test.ts +37 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.cache-stale-insert.coverage.test.ts +39 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.coverage.test.ts +334 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.delete-fixup.coverage.test.ts +68 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.delete-successor.coverage.test.ts +75 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.factories.coverage.test.ts +26 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-compare-update.coverage.test.ts +74 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-no-update.coverage.test.ts +44 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-nullish.coverage.test.ts +61 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-mapmode-defined.coverage.test.ts +35 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-mapmode-undefined.coverage.test.ts +43 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-more.coverage.test.ts +99 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint.coverage.test.ts +60 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.insert-cache-nullish.coverage.test.ts +29 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.insert-header-parent-nullish.coverage.test.ts +17 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.internal-walk.coverage.test.ts +57 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.minmax-cache.test.ts +65 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.misc-inputs.coverage.test.ts +17 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-2.coverage.test.ts +121 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-3.coverage.test.ts +55 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-4.coverage.test.ts +44 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.predsucc.coverage.test.ts +40 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.remaining-branches.coverage.test.ts +123 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.set-inputs.coverage.test.ts +64 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-parent-cache.coverage.test.ts +79 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-remaining.coverage.test.ts +44 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-uncovered.coverage.test.ts +74 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.update-branches.coverage.test.ts +30 -0
- package/test/unit/data-structures/binary-tree/segment-tree.more-branches.coverage.test.ts +31 -0
- package/test/unit/data-structures/binary-tree/tree-counter.coverage.test.ts +115 -0
- package/test/unit/data-structures/binary-tree/tree-counter.more-branches.coverage.test.ts +244 -0
- package/test/unit/data-structures/binary-tree/tree-counter.test.ts +4 -2
- package/test/unit/data-structures/binary-tree/tree-multi-map.coverage.test.ts +104 -0
- package/test/unit/data-structures/binary-tree/tree-multi-map.more-branches-2.coverage.test.ts +59 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches-2.coverage.test.ts +40 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches-3.coverage.test.ts +65 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches-4.coverage.test.ts +98 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches-5.coverage.test.ts +51 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches.coverage.test.ts +62 -0
- package/test/unit/data-structures/graph/directed-graph.more-branches-2.coverage.test.ts +38 -0
- package/test/unit/data-structures/graph/directed-graph.more-branches-3.coverage.test.ts +25 -0
- package/test/unit/data-structures/graph/directed-graph.more-branches.coverage.test.ts +82 -0
- package/test/unit/data-structures/graph/map-graph.more-branches.coverage.test.ts +22 -0
- package/test/unit/data-structures/graph/undirected-graph.more-branches-2.coverage.test.ts +35 -0
- package/test/unit/data-structures/graph/undirected-graph.more-branches.coverage.test.ts +87 -0
- package/test/unit/data-structures/hash/hash-map.more-branches.coverage.test.ts +64 -0
- package/test/unit/data-structures/hash/hash-map.toEntryFn-branch.coverage.test.ts +9 -0
- package/test/unit/data-structures/heap/heap.misc-branches.coverage.test.ts +110 -0
- package/test/unit/data-structures/heap/heap.remaining-branches.coverage.test.ts +22 -0
- package/test/unit/data-structures/heap/max-heap.coverage.test.ts +29 -0
- package/test/unit/data-structures/linked-list/doubly-linked-list.more-branches.coverage.test.ts +72 -0
- package/test/unit/data-structures/linked-list/linked-list.unshiftMany-else.coverage.test.ts +15 -0
- package/test/unit/data-structures/linked-list/singly-linked-list.coverage.test.ts +221 -0
- package/test/unit/data-structures/linked-list/singly-linked-list.more-branches.coverage.test.ts +86 -0
- package/test/unit/data-structures/linked-list/skip-linked-list.more-branches.coverage.test.ts +31 -0
- package/test/unit/data-structures/matrix/matrix.more-branches.coverage.test.ts +81 -0
- package/test/unit/data-structures/matrix/matrix.pivotElement-nullish.coverage.test.ts +28 -0
- package/test/unit/data-structures/priority-queue/max-priority-queue.more-branches.coverage.test.ts +10 -0
- package/test/unit/data-structures/priority-queue/priority-queue.coverage.test.ts +21 -0
- package/test/unit/data-structures/queue/deque.coverage.test.ts +173 -0
- package/test/unit/data-structures/queue/deque.more-branches-2.coverage.test.ts +39 -0
- package/test/unit/data-structures/queue/deque.more-branches-3.coverage.test.ts +9 -0
- package/test/unit/data-structures/queue/deque.more-branches.coverage.test.ts +95 -0
- package/test/unit/data-structures/queue/queue.coverage.test.ts +138 -0
- package/test/unit/data-structures/queue/queue.more-branches-2.coverage.test.ts +27 -0
- package/test/unit/data-structures/stack/stack.coverage.test.ts +112 -0
- package/test/unit/data-structures/tree/tree.more-branches.coverage.test.ts +9 -0
- package/test/unit/data-structures/trie/trie.more-branches-2.coverage.test.ts +51 -0
- package/test/utils/patch.ts +33 -0
package/dist/esm/index.mjs
CHANGED
|
@@ -1402,6 +1402,12 @@ var LinearLinkedBase = class extends LinearBase {
|
|
|
1402
1402
|
}
|
|
1403
1403
|
return -1;
|
|
1404
1404
|
}
|
|
1405
|
+
/**
|
|
1406
|
+
* Concatenate lists/elements preserving order.
|
|
1407
|
+
* @param items - Elements or `LinearBase` instances.
|
|
1408
|
+
* @returns New list with combined elements (`this` type).
|
|
1409
|
+
* @remarks Time O(sum(length)), Space O(sum(length))
|
|
1410
|
+
*/
|
|
1405
1411
|
concat(...items) {
|
|
1406
1412
|
const newList = this.clone();
|
|
1407
1413
|
for (const item of items) {
|
|
@@ -4228,6 +4234,12 @@ var Deque = class extends LinearBase {
|
|
|
4228
4234
|
*/
|
|
4229
4235
|
_setBucketSize(size) {
|
|
4230
4236
|
this._bucketSize = size;
|
|
4237
|
+
if (this._length === 0) {
|
|
4238
|
+
this._buckets = [new Array(this._bucketSize)];
|
|
4239
|
+
this._bucketCount = 1;
|
|
4240
|
+
this._bucketFirst = this._bucketLast = 0;
|
|
4241
|
+
this._firstInBucket = this._lastInBucket = this._bucketSize >> 1;
|
|
4242
|
+
}
|
|
4231
4243
|
}
|
|
4232
4244
|
/**
|
|
4233
4245
|
* (Protected) Iterate elements from front to back.
|
|
@@ -7283,7 +7295,6 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
7283
7295
|
* @remarks Time O(N * M), where N is the number of items to set and M is the size of the tree at insertion (due to O(M) `set` operation). Space O(M) (from `set`) + O(N) (for the `inserted` array).
|
|
7284
7296
|
*
|
|
7285
7297
|
* @param keysNodesEntriesOrRaws - An iterable of items to set.
|
|
7286
|
-
* @param [values] - An optional parallel iterable of values.
|
|
7287
7298
|
* @returns An array of booleans indicating the success of each individual `set` operation.
|
|
7288
7299
|
*/
|
|
7289
7300
|
addMany(keysNodesEntriesOrRaws) {
|
|
@@ -7340,13 +7351,13 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
7340
7351
|
* Deletes a node from the tree.
|
|
7341
7352
|
* @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).
|
|
7342
7353
|
*
|
|
7343
|
-
* @param
|
|
7354
|
+
* @param keyNodeEntryRawOrPredicate - The node to delete.
|
|
7344
7355
|
* @returns An array containing deletion results (for compatibility with self-balancing trees).
|
|
7345
7356
|
*/
|
|
7346
|
-
delete(
|
|
7357
|
+
delete(keyNodeEntryRawOrPredicate) {
|
|
7347
7358
|
const deletedResult = [];
|
|
7348
7359
|
if (!this._root) return deletedResult;
|
|
7349
|
-
const curr = this.getNode(
|
|
7360
|
+
const curr = this.getNode(keyNodeEntryRawOrPredicate);
|
|
7350
7361
|
if (!curr) return deletedResult;
|
|
7351
7362
|
const parent = curr?.parent;
|
|
7352
7363
|
let needBalanced;
|
|
@@ -7529,7 +7540,7 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
7529
7540
|
}
|
|
7530
7541
|
return true;
|
|
7531
7542
|
}, "checkBST");
|
|
7532
|
-
const isStandardBST = checkBST(
|
|
7543
|
+
const isStandardBST = checkBST();
|
|
7533
7544
|
const isInverseBST = checkBST(true);
|
|
7534
7545
|
return isStandardBST || isInverseBST;
|
|
7535
7546
|
}
|
|
@@ -8767,7 +8778,39 @@ var BST = class extends BinaryTree {
|
|
|
8767
8778
|
* @returns The first matching node, or undefined if not found.
|
|
8768
8779
|
*/
|
|
8769
8780
|
getNode(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
|
|
8770
|
-
|
|
8781
|
+
if (keyNodeEntryOrPredicate === null || keyNodeEntryOrPredicate === void 0) return void 0;
|
|
8782
|
+
if (this._isPredicate(keyNodeEntryOrPredicate)) {
|
|
8783
|
+
return this.getNodes(keyNodeEntryOrPredicate, true, startNode, iterationType)[0] ?? void 0;
|
|
8784
|
+
}
|
|
8785
|
+
if (keyNodeEntryOrPredicate instanceof Range) {
|
|
8786
|
+
return this.getNodes(
|
|
8787
|
+
keyNodeEntryOrPredicate,
|
|
8788
|
+
true,
|
|
8789
|
+
startNode,
|
|
8790
|
+
iterationType
|
|
8791
|
+
)[0] ?? void 0;
|
|
8792
|
+
}
|
|
8793
|
+
let targetKey;
|
|
8794
|
+
if (this.isNode(keyNodeEntryOrPredicate)) {
|
|
8795
|
+
targetKey = keyNodeEntryOrPredicate.key;
|
|
8796
|
+
} else if (this.isEntry(keyNodeEntryOrPredicate)) {
|
|
8797
|
+
const k = keyNodeEntryOrPredicate[0];
|
|
8798
|
+
if (k === null || k === void 0) return void 0;
|
|
8799
|
+
targetKey = k;
|
|
8800
|
+
} else {
|
|
8801
|
+
targetKey = keyNodeEntryOrPredicate;
|
|
8802
|
+
}
|
|
8803
|
+
const start = this.ensureNode(startNode);
|
|
8804
|
+
if (!start) return void 0;
|
|
8805
|
+
const NIL = this._NIL;
|
|
8806
|
+
let cur = start;
|
|
8807
|
+
const cmpFn = this._comparator;
|
|
8808
|
+
while (cur && cur !== NIL) {
|
|
8809
|
+
const c = cmpFn(targetKey, cur.key);
|
|
8810
|
+
if (c === 0) return cur;
|
|
8811
|
+
cur = c < 0 ? cur._left : cur._right;
|
|
8812
|
+
}
|
|
8813
|
+
return void 0;
|
|
8771
8814
|
}
|
|
8772
8815
|
/**
|
|
8773
8816
|
* Searches the tree for nodes matching a predicate, key, or range.
|
|
@@ -8788,8 +8831,30 @@ var BST = class extends BinaryTree {
|
|
|
8788
8831
|
if (keyNodeEntryOrPredicate === null) return [];
|
|
8789
8832
|
startNode = this.ensureNode(startNode);
|
|
8790
8833
|
if (!startNode) return [];
|
|
8791
|
-
let predicate;
|
|
8792
8834
|
const isRange = this.isRange(keyNodeEntryOrPredicate);
|
|
8835
|
+
const isPred = !isRange && this._isPredicate(keyNodeEntryOrPredicate);
|
|
8836
|
+
if (!isRange && !isPred) {
|
|
8837
|
+
let targetKey;
|
|
8838
|
+
if (this.isNode(keyNodeEntryOrPredicate)) {
|
|
8839
|
+
targetKey = keyNodeEntryOrPredicate.key;
|
|
8840
|
+
} else if (this.isEntry(keyNodeEntryOrPredicate)) {
|
|
8841
|
+
const k = keyNodeEntryOrPredicate[0];
|
|
8842
|
+
if (k !== null && k !== void 0) targetKey = k;
|
|
8843
|
+
} else {
|
|
8844
|
+
targetKey = keyNodeEntryOrPredicate;
|
|
8845
|
+
}
|
|
8846
|
+
if (targetKey === void 0) return [];
|
|
8847
|
+
const NIL = this._NIL;
|
|
8848
|
+
const cmpFn = this._comparator;
|
|
8849
|
+
let cur = startNode;
|
|
8850
|
+
while (cur && cur !== NIL) {
|
|
8851
|
+
const c = cmpFn(targetKey, cur.key);
|
|
8852
|
+
if (c === 0) return [callback(cur)];
|
|
8853
|
+
cur = c < 0 ? cur._left : cur._right;
|
|
8854
|
+
}
|
|
8855
|
+
return [];
|
|
8856
|
+
}
|
|
8857
|
+
let predicate;
|
|
8793
8858
|
if (isRange) {
|
|
8794
8859
|
predicate = /* @__PURE__ */ __name((node) => {
|
|
8795
8860
|
if (!node) return false;
|
|
@@ -9711,7 +9776,7 @@ var BST = class extends BinaryTree {
|
|
|
9711
9776
|
succ.left = node.left;
|
|
9712
9777
|
if (succ.left) succ.left.parent = succ;
|
|
9713
9778
|
}
|
|
9714
|
-
this._size = Math.max(0,
|
|
9779
|
+
this._size = Math.max(0, this._size - 1);
|
|
9715
9780
|
return true;
|
|
9716
9781
|
}
|
|
9717
9782
|
};
|
|
@@ -10827,12 +10892,11 @@ var RedBlackTreeNode = class {
|
|
|
10827
10892
|
value;
|
|
10828
10893
|
parent = void 0;
|
|
10829
10894
|
/**
|
|
10830
|
-
* Create a Red-Black Tree
|
|
10831
|
-
* @remarks Time O(
|
|
10832
|
-
* @param key -
|
|
10833
|
-
* @param [value]-
|
|
10834
|
-
* @param color -
|
|
10835
|
-
* @returns New RedBlackTree instance.
|
|
10895
|
+
* Create a Red-Black Tree node.
|
|
10896
|
+
* @remarks Time O(1), Space O(1)
|
|
10897
|
+
* @param key - Node key.
|
|
10898
|
+
* @param [value] - Node value (unused in map mode trees).
|
|
10899
|
+
* @param color - Node color.
|
|
10836
10900
|
*/
|
|
10837
10901
|
constructor(key, value, color = "BLACK") {
|
|
10838
10902
|
this.key = key;
|
|
@@ -10963,11 +11027,33 @@ var RedBlackTree = class extends BST {
|
|
|
10963
11027
|
constructor(keysNodesEntriesOrRaws = [], options) {
|
|
10964
11028
|
super([], options);
|
|
10965
11029
|
this._root = this.NIL;
|
|
11030
|
+
this._header = new RedBlackTreeNode(void 0, void 0, "BLACK");
|
|
11031
|
+
this._header.parent = this.NIL;
|
|
11032
|
+
this._header._left = this.NIL;
|
|
11033
|
+
this._header._right = this.NIL;
|
|
10966
11034
|
if (keysNodesEntriesOrRaws) {
|
|
10967
11035
|
this.setMany(keysNodesEntriesOrRaws);
|
|
10968
11036
|
}
|
|
10969
11037
|
}
|
|
10970
11038
|
_root;
|
|
11039
|
+
/**
|
|
11040
|
+
* (Internal) Header sentinel:
|
|
11041
|
+
* - header.parent -> root
|
|
11042
|
+
* - header._left -> min (or NIL)
|
|
11043
|
+
* - header._right -> max (or NIL)
|
|
11044
|
+
*
|
|
11045
|
+
* IMPORTANT:
|
|
11046
|
+
* - This header is NOT part of the actual tree.
|
|
11047
|
+
* - Do NOT use `header.left` / `header.right` accessors for wiring: those setters update `NIL.parent`
|
|
11048
|
+
* and can corrupt sentinel invariants / cause hangs. Only touch `header._left/_right`.
|
|
11049
|
+
*/
|
|
11050
|
+
_header;
|
|
11051
|
+
/**
|
|
11052
|
+
* (Internal) Cache of the current minimum and maximum nodes.
|
|
11053
|
+
* Used for fast-path insert/update when keys are monotonic or near-boundary.
|
|
11054
|
+
*/
|
|
11055
|
+
_minNode;
|
|
11056
|
+
_maxNode;
|
|
10971
11057
|
/**
|
|
10972
11058
|
* Get the current root node.
|
|
10973
11059
|
* @remarks Time O(1), Space O(1)
|
|
@@ -11001,18 +11087,387 @@ var RedBlackTree = class extends BST {
|
|
|
11001
11087
|
* @remarks Time O(n), Space O(1)
|
|
11002
11088
|
* @returns void
|
|
11003
11089
|
*/
|
|
11090
|
+
/**
|
|
11091
|
+
* Remove all nodes and clear internal caches.
|
|
11092
|
+
* @remarks Time O(n) average, Space O(1)
|
|
11093
|
+
*/
|
|
11004
11094
|
clear() {
|
|
11005
11095
|
super.clear();
|
|
11006
11096
|
this._root = this.NIL;
|
|
11097
|
+
this._header.parent = this.NIL;
|
|
11098
|
+
this._setMinCache(void 0);
|
|
11099
|
+
this._setMaxCache(void 0);
|
|
11007
11100
|
}
|
|
11008
11101
|
/**
|
|
11009
|
-
*
|
|
11010
|
-
*
|
|
11011
|
-
*
|
|
11012
|
-
* @
|
|
11013
|
-
|
|
11102
|
+
* (Internal) Find a node by key using a tight BST walk (no allocations).
|
|
11103
|
+
*
|
|
11104
|
+
* NOTE: This uses `header.parent` as the canonical root pointer.
|
|
11105
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11106
|
+
*/
|
|
11107
|
+
_findNodeByKey(key) {
|
|
11108
|
+
const NIL = this.NIL;
|
|
11109
|
+
const cmp = this._compare.bind(this);
|
|
11110
|
+
let cur = this._header.parent ?? NIL;
|
|
11111
|
+
while (cur !== NIL) {
|
|
11112
|
+
const c = cmp(key, cur.key);
|
|
11113
|
+
if (c < 0) cur = cur.left ?? NIL;
|
|
11114
|
+
else if (c > 0) cur = cur.right ?? NIL;
|
|
11115
|
+
else return cur;
|
|
11116
|
+
}
|
|
11117
|
+
return void 0;
|
|
11118
|
+
}
|
|
11119
|
+
/**
|
|
11120
|
+
* (Internal) In-order predecessor of a node in a BST.
|
|
11121
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11122
|
+
*/
|
|
11123
|
+
_predecessorOf(node) {
|
|
11124
|
+
const NIL = this.NIL;
|
|
11125
|
+
if (node.left && node.left !== NIL) {
|
|
11126
|
+
let cur2 = node.left;
|
|
11127
|
+
while (cur2.right && cur2.right !== NIL) cur2 = cur2.right;
|
|
11128
|
+
return cur2;
|
|
11129
|
+
}
|
|
11130
|
+
let cur = node;
|
|
11131
|
+
let p = node.parent;
|
|
11132
|
+
while (p && cur === p.left) {
|
|
11133
|
+
cur = p;
|
|
11134
|
+
p = p.parent;
|
|
11135
|
+
}
|
|
11136
|
+
return p;
|
|
11137
|
+
}
|
|
11138
|
+
/**
|
|
11139
|
+
* (Internal) In-order successor of a node in a BST.
|
|
11140
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11141
|
+
*/
|
|
11142
|
+
_successorOf(node) {
|
|
11143
|
+
const NIL = this.NIL;
|
|
11144
|
+
if (node.right && node.right !== NIL) {
|
|
11145
|
+
let cur2 = node.right;
|
|
11146
|
+
while (cur2.left && cur2.left !== NIL) cur2 = cur2.left;
|
|
11147
|
+
return cur2;
|
|
11148
|
+
}
|
|
11149
|
+
let cur = node;
|
|
11150
|
+
let p = node.parent;
|
|
11151
|
+
while (p && cur === p.right) {
|
|
11152
|
+
cur = p;
|
|
11153
|
+
p = p.parent;
|
|
11154
|
+
}
|
|
11155
|
+
return p;
|
|
11156
|
+
}
|
|
11157
|
+
/**
|
|
11158
|
+
* (Internal) Attach a new node directly under a known parent/side (no search).
|
|
11159
|
+
*
|
|
11160
|
+
* This is a performance-oriented helper used by boundary fast paths and hinted insertion.
|
|
11161
|
+
* It will:
|
|
11162
|
+
* - wire parent/child pointers (using accessors, so parent pointers are updated)
|
|
11163
|
+
* - initialize children to NIL
|
|
11164
|
+
* - mark the new node RED, then run insert fix-up
|
|
11165
|
+
*
|
|
11166
|
+
* Precondition: the chosen slot (parent.left/parent.right) is empty (NIL/null/undefined).
|
|
11167
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11168
|
+
*/
|
|
11169
|
+
_attachNewNode(parent, side, node) {
|
|
11170
|
+
const NIL = this.NIL;
|
|
11171
|
+
node.parent = parent;
|
|
11172
|
+
if (side === "left") parent.left = node;
|
|
11173
|
+
else parent.right = node;
|
|
11174
|
+
node.left = NIL;
|
|
11175
|
+
node.right = NIL;
|
|
11176
|
+
node.color = "RED";
|
|
11177
|
+
this._insertFixup(node);
|
|
11178
|
+
if (this.isRealNode(this._root)) this._root.color = "BLACK";
|
|
11179
|
+
}
|
|
11180
|
+
/**
|
|
11181
|
+
* (Internal) a single source of truth for min/max is header._left/_right.
|
|
11182
|
+
* Keep legacy _minNode/_maxNode mirrored for compatibility.
|
|
11183
|
+
* @remarks Time O(1), Space O(1)
|
|
11184
|
+
*/
|
|
11185
|
+
/**
|
|
11186
|
+
* (Internal) Update min cache pointers (header._left is the canonical min pointer).
|
|
11187
|
+
* @remarks Time O(1), Space O(1)
|
|
11188
|
+
*/
|
|
11189
|
+
_setMinCache(node) {
|
|
11190
|
+
this._minNode = node;
|
|
11191
|
+
this._header._left = node ?? this.NIL;
|
|
11192
|
+
}
|
|
11193
|
+
/**
|
|
11194
|
+
* (Internal) Update max cache pointers (header._right is the canonical max pointer).
|
|
11195
|
+
* @remarks Time O(1), Space O(1)
|
|
11196
|
+
*/
|
|
11197
|
+
_setMaxCache(node) {
|
|
11198
|
+
this._maxNode = node;
|
|
11199
|
+
this._header._right = node ?? this.NIL;
|
|
11200
|
+
}
|
|
11201
|
+
/**
|
|
11202
|
+
* (Internal) Core set implementation returning the affected node.
|
|
11203
|
+
*
|
|
11204
|
+
* Hot path goals:
|
|
11205
|
+
* - Avoid double walks (search+insert): do a single traversal that either updates or inserts.
|
|
11206
|
+
* - Use header min/max caches to fast-path boundary inserts.
|
|
11207
|
+
* - Keep header._left/_right as canonical min/max pointers.
|
|
11208
|
+
*
|
|
11209
|
+
* Return value:
|
|
11210
|
+
* - `{ node, created:false }` when an existing key is updated
|
|
11211
|
+
* - `{ node, created:true }` when a new node is inserted
|
|
11212
|
+
* - `undefined` only on unexpected internal failure.
|
|
11213
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11214
|
+
*/
|
|
11215
|
+
_setKVNode(key, nextValue) {
|
|
11216
|
+
const NIL = this.NIL;
|
|
11217
|
+
const comparator = this._comparator;
|
|
11218
|
+
const header = this._header;
|
|
11219
|
+
const minN = header._left ?? NIL;
|
|
11220
|
+
if (minN !== NIL) {
|
|
11221
|
+
const cMin = comparator(key, minN.key);
|
|
11222
|
+
if (cMin === 0) {
|
|
11223
|
+
if (this._isMapMode) {
|
|
11224
|
+
if (nextValue !== void 0) this._store.set(key, nextValue);
|
|
11225
|
+
else this._setValue(key, nextValue);
|
|
11226
|
+
} else minN.value = nextValue;
|
|
11227
|
+
return { node: minN, created: false };
|
|
11228
|
+
}
|
|
11229
|
+
const minL = minN.left;
|
|
11230
|
+
if (cMin < 0 && (minL === NIL || minL === null || minL === void 0)) {
|
|
11231
|
+
const newNode2 = this.createNode(key, nextValue);
|
|
11232
|
+
this._attachNewNode(minN, "left", newNode2);
|
|
11233
|
+
if (this._isMapMode) {
|
|
11234
|
+
if (nextValue !== void 0) this._store.set(newNode2.key, nextValue);
|
|
11235
|
+
else this._setValue(newNode2.key, nextValue);
|
|
11236
|
+
}
|
|
11237
|
+
this._size++;
|
|
11238
|
+
this._setMinCache(newNode2);
|
|
11239
|
+
if (header._right === NIL) this._setMaxCache(newNode2);
|
|
11240
|
+
return { node: newNode2, created: true };
|
|
11241
|
+
}
|
|
11242
|
+
if (cMin > 0) {
|
|
11243
|
+
const maxN = header._right ?? NIL;
|
|
11244
|
+
const cMax = comparator(key, maxN.key);
|
|
11245
|
+
if (cMax === 0) {
|
|
11246
|
+
if (this._isMapMode) {
|
|
11247
|
+
if (nextValue !== void 0) this._store.set(key, nextValue);
|
|
11248
|
+
else this._setValue(key, nextValue);
|
|
11249
|
+
} else maxN.value = nextValue;
|
|
11250
|
+
return { node: maxN, created: false };
|
|
11251
|
+
}
|
|
11252
|
+
const maxR = maxN.right;
|
|
11253
|
+
if (cMax > 0 && (maxR === NIL || maxR === null || maxR === void 0)) {
|
|
11254
|
+
const newNode2 = this.createNode(key, nextValue);
|
|
11255
|
+
this._attachNewNode(maxN, "right", newNode2);
|
|
11256
|
+
if (this._isMapMode) {
|
|
11257
|
+
if (nextValue !== void 0) this._store.set(newNode2.key, nextValue);
|
|
11258
|
+
else this._setValue(newNode2.key, nextValue);
|
|
11259
|
+
}
|
|
11260
|
+
this._size++;
|
|
11261
|
+
this._setMaxCache(newNode2);
|
|
11262
|
+
if (header._left === NIL) this._setMinCache(newNode2);
|
|
11263
|
+
return { node: newNode2, created: true };
|
|
11264
|
+
}
|
|
11265
|
+
}
|
|
11266
|
+
}
|
|
11267
|
+
const cmp = comparator;
|
|
11268
|
+
const isMapMode = this._isMapMode;
|
|
11269
|
+
const store = this._store;
|
|
11270
|
+
let current = this._header.parent ?? NIL;
|
|
11271
|
+
let parent;
|
|
11272
|
+
let lastCompared = 0;
|
|
11273
|
+
while (current !== NIL) {
|
|
11274
|
+
parent = current;
|
|
11275
|
+
lastCompared = cmp(key, current.key);
|
|
11276
|
+
if (lastCompared < 0) current = current.left ?? NIL;
|
|
11277
|
+
else if (lastCompared > 0) current = current.right ?? NIL;
|
|
11278
|
+
else {
|
|
11279
|
+
if (isMapMode) {
|
|
11280
|
+
if (nextValue !== void 0) store.set(key, nextValue);
|
|
11281
|
+
else this._setValue(key, nextValue);
|
|
11282
|
+
} else {
|
|
11283
|
+
current.value = nextValue;
|
|
11284
|
+
}
|
|
11285
|
+
return { node: current, created: false };
|
|
11286
|
+
}
|
|
11287
|
+
}
|
|
11288
|
+
const newNode = this.createNode(key, nextValue);
|
|
11289
|
+
newNode.parent = parent;
|
|
11290
|
+
if (!parent) {
|
|
11291
|
+
this._setRoot(newNode);
|
|
11292
|
+
} else if (lastCompared < 0) {
|
|
11293
|
+
parent.left = newNode;
|
|
11294
|
+
} else {
|
|
11295
|
+
parent.right = newNode;
|
|
11296
|
+
}
|
|
11297
|
+
newNode.left = NIL;
|
|
11298
|
+
newNode.right = NIL;
|
|
11299
|
+
newNode.color = "RED";
|
|
11300
|
+
this._insertFixup(newNode);
|
|
11301
|
+
if (this.isRealNode(this._root)) this._root.color = "BLACK";
|
|
11302
|
+
else return void 0;
|
|
11303
|
+
if (isMapMode) {
|
|
11304
|
+
if (nextValue !== void 0) store.set(newNode.key, nextValue);
|
|
11305
|
+
else this._setValue(newNode.key, nextValue);
|
|
11306
|
+
}
|
|
11307
|
+
this._size++;
|
|
11308
|
+
const hMin = this._header._left ?? NIL;
|
|
11309
|
+
const hMax = this._header._right ?? NIL;
|
|
11310
|
+
if (hMin === NIL || hMax === NIL) {
|
|
11311
|
+
this._setMinCache(newNode);
|
|
11312
|
+
this._setMaxCache(newNode);
|
|
11313
|
+
} else if (parent === hMax && lastCompared > 0) {
|
|
11314
|
+
this._setMaxCache(newNode);
|
|
11315
|
+
} else if (parent === hMin && lastCompared < 0) {
|
|
11316
|
+
this._setMinCache(newNode);
|
|
11317
|
+
} else {
|
|
11318
|
+
if (cmp(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
11319
|
+
if (cmp(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
11320
|
+
}
|
|
11321
|
+
return { node: newNode, created: true };
|
|
11322
|
+
}
|
|
11323
|
+
/**
|
|
11324
|
+
* (Internal) Boolean wrapper around `_setKVNode`.
|
|
11325
|
+
*
|
|
11326
|
+
* Includes a map-mode update fast-path:
|
|
11327
|
+
* - If `isMapMode=true` and the key already exists in `_store`, then updating the value does not
|
|
11328
|
+
* require any tree search/rotation (tree shape depends only on key).
|
|
11329
|
+
* - This path is intentionally limited to `nextValue !== undefined` to preserve existing
|
|
11330
|
+
* semantics for `undefined` values.
|
|
11331
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11332
|
+
*/
|
|
11333
|
+
_setKV(key, nextValue) {
|
|
11334
|
+
if (this._isMapMode && nextValue !== void 0) {
|
|
11335
|
+
const store = this._store;
|
|
11336
|
+
if (store.has(key)) {
|
|
11337
|
+
store.set(key, nextValue);
|
|
11338
|
+
return true;
|
|
11339
|
+
}
|
|
11340
|
+
}
|
|
11341
|
+
return this._setKVNode(key, nextValue) !== void 0;
|
|
11342
|
+
}
|
|
11343
|
+
/**
|
|
11344
|
+
* Insert/update using a hint node to speed up nearby insertions.
|
|
11345
|
+
*
|
|
11346
|
+
* close to the expected insertion position (often the previously returned node in a loop).
|
|
11347
|
+
*
|
|
11348
|
+
* When the hint is a good fit (sorted / nearly-sorted insertion), this can avoid most of the
|
|
11349
|
+
* normal root-to-leaf search and reduce constant factors.
|
|
11350
|
+
*
|
|
11351
|
+
* When the hint does not match (random workloads), this will fall back to the normal set path.
|
|
11352
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11353
|
+
*/
|
|
11354
|
+
setWithHintNode(key, value, hint) {
|
|
11355
|
+
if (!hint || !this.isRealNode(hint)) {
|
|
11356
|
+
return this._setKVNode(key, value)?.node;
|
|
11357
|
+
}
|
|
11358
|
+
const cmp = this._compare.bind(this);
|
|
11359
|
+
const c0 = cmp(key, hint.key);
|
|
11360
|
+
if (c0 === 0) {
|
|
11361
|
+
if (this._isMapMode) {
|
|
11362
|
+
if (value !== void 0) this._store.set(key, value);
|
|
11363
|
+
else this._setValue(key, value);
|
|
11364
|
+
} else hint.value = value;
|
|
11365
|
+
return hint;
|
|
11366
|
+
}
|
|
11367
|
+
if (c0 < 0) {
|
|
11368
|
+
if (!this.isRealNode(hint.left)) {
|
|
11369
|
+
const newNode = this.createNode(key, value);
|
|
11370
|
+
if (!this.isRealNode(newNode)) return void 0;
|
|
11371
|
+
this._attachNewNode(hint, "left", newNode);
|
|
11372
|
+
if (this._isMapMode) {
|
|
11373
|
+
if (value !== void 0) this._store.set(key, value);
|
|
11374
|
+
else this._setValue(key, value);
|
|
11375
|
+
}
|
|
11376
|
+
this._size++;
|
|
11377
|
+
const NIL = this.NIL;
|
|
11378
|
+
const hMin = this._header._left ?? NIL;
|
|
11379
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
11380
|
+
const hMax = this._header._right ?? NIL;
|
|
11381
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
11382
|
+
return newNode;
|
|
11383
|
+
}
|
|
11384
|
+
const pred = this._predecessorOf(hint);
|
|
11385
|
+
if (pred && cmp(pred.key, key) >= 0) {
|
|
11386
|
+
return this._setKVNode(key, value)?.node;
|
|
11387
|
+
}
|
|
11388
|
+
if (pred && !this.isRealNode(pred.right)) {
|
|
11389
|
+
const newNode = this.createNode(key, value);
|
|
11390
|
+
if (!this.isRealNode(newNode)) return void 0;
|
|
11391
|
+
this._attachNewNode(pred, "right", newNode);
|
|
11392
|
+
if (this._isMapMode) {
|
|
11393
|
+
if (value !== void 0) this._store.set(key, value);
|
|
11394
|
+
else this._setValue(key, value);
|
|
11395
|
+
}
|
|
11396
|
+
this._size++;
|
|
11397
|
+
const NIL = this.NIL;
|
|
11398
|
+
const hMin = this._header._left ?? NIL;
|
|
11399
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
11400
|
+
const hMax = this._header._right ?? NIL;
|
|
11401
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
11402
|
+
return newNode;
|
|
11403
|
+
}
|
|
11404
|
+
return this._setKVNode(key, value)?.node;
|
|
11405
|
+
}
|
|
11406
|
+
if (!this.isRealNode(hint.right)) {
|
|
11407
|
+
const newNode = this.createNode(key, value);
|
|
11408
|
+
if (!this.isRealNode(newNode)) return void 0;
|
|
11409
|
+
this._attachNewNode(hint, "right", newNode);
|
|
11410
|
+
if (this._isMapMode) {
|
|
11411
|
+
if (value !== void 0) this._store.set(key, value);
|
|
11412
|
+
else this._setValue(key, value);
|
|
11413
|
+
}
|
|
11414
|
+
this._size++;
|
|
11415
|
+
const NIL = this.NIL;
|
|
11416
|
+
const hMin = this._header._left ?? NIL;
|
|
11417
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
11418
|
+
const hMax = this._header._right ?? NIL;
|
|
11419
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
11420
|
+
return newNode;
|
|
11421
|
+
}
|
|
11422
|
+
const succ = this._successorOf(hint);
|
|
11423
|
+
if (succ && cmp(succ.key, key) <= 0) {
|
|
11424
|
+
return this._setKVNode(key, value)?.node;
|
|
11425
|
+
}
|
|
11426
|
+
if (succ && !this.isRealNode(succ.left)) {
|
|
11427
|
+
const newNode = this.createNode(key, value);
|
|
11428
|
+
if (!this.isRealNode(newNode)) return void 0;
|
|
11429
|
+
this._attachNewNode(succ, "left", newNode);
|
|
11430
|
+
if (this._isMapMode) {
|
|
11431
|
+
if (value !== void 0) this._store.set(key, value);
|
|
11432
|
+
else this._setValue(key, value);
|
|
11433
|
+
}
|
|
11434
|
+
this._size++;
|
|
11435
|
+
const NIL = this.NIL;
|
|
11436
|
+
const hMin = this._header._left ?? NIL;
|
|
11437
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
11438
|
+
const hMax = this._header._right ?? NIL;
|
|
11439
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
11440
|
+
return newNode;
|
|
11441
|
+
}
|
|
11442
|
+
return this._setKVNode(key, value)?.node;
|
|
11443
|
+
}
|
|
11444
|
+
/**
|
|
11445
|
+
* Boolean wrapper for setWithHintNode.
|
|
11446
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11447
|
+
*/
|
|
11448
|
+
setWithHint(key, value, hint) {
|
|
11449
|
+
return this.setWithHintNode(key, value, hint) !== void 0;
|
|
11450
|
+
}
|
|
11451
|
+
/**
|
|
11452
|
+
* Insert or update a key/value (map mode) or key-only (set mode).
|
|
11453
|
+
*
|
|
11454
|
+
* This method is optimized for:
|
|
11455
|
+
* - monotonic inserts via min/max boundary fast paths
|
|
11456
|
+
* - updates via a single-pass search (no double walk)
|
|
11457
|
+
*
|
|
11458
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11014
11459
|
*/
|
|
11015
11460
|
set(keyNodeOrEntry, value) {
|
|
11461
|
+
if (!this.isNode(keyNodeOrEntry)) {
|
|
11462
|
+
if (keyNodeOrEntry === null || keyNodeOrEntry === void 0) return false;
|
|
11463
|
+
if (this.isEntry(keyNodeOrEntry)) {
|
|
11464
|
+
const key = keyNodeOrEntry[0];
|
|
11465
|
+
if (key === null || key === void 0) return false;
|
|
11466
|
+
const nextValue = value ?? keyNodeOrEntry[1];
|
|
11467
|
+
return this._setKV(key, nextValue);
|
|
11468
|
+
}
|
|
11469
|
+
return this._setKV(keyNodeOrEntry, value);
|
|
11470
|
+
}
|
|
11016
11471
|
const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
|
11017
11472
|
if (!this.isRealNode(newNode)) return false;
|
|
11018
11473
|
const insertStatus = this._insert(newNode);
|
|
@@ -11034,19 +11489,23 @@ var RedBlackTree = class extends BST {
|
|
|
11034
11489
|
}
|
|
11035
11490
|
/**
|
|
11036
11491
|
* Delete a node by key/node/entry and rebalance as needed.
|
|
11037
|
-
* @remarks Time O(log n), Space O(1)
|
|
11038
|
-
* @param
|
|
11492
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11493
|
+
* @param keyNodeEntryRawOrPredicate - Key, node, or [key, value] entry identifying the node to delete.
|
|
11039
11494
|
* @returns Array with deletion metadata (removed node, rebalancing hint if any).
|
|
11040
11495
|
*/
|
|
11041
|
-
delete(
|
|
11042
|
-
if (
|
|
11496
|
+
delete(keyNodeEntryRawOrPredicate) {
|
|
11497
|
+
if (keyNodeEntryRawOrPredicate === null) return [];
|
|
11043
11498
|
const results = [];
|
|
11044
11499
|
let nodeToDelete;
|
|
11045
|
-
if (this._isPredicate(
|
|
11046
|
-
else nodeToDelete = this.isRealNode(
|
|
11500
|
+
if (this._isPredicate(keyNodeEntryRawOrPredicate)) nodeToDelete = this.getNode(keyNodeEntryRawOrPredicate);
|
|
11501
|
+
else nodeToDelete = this.isRealNode(keyNodeEntryRawOrPredicate) ? keyNodeEntryRawOrPredicate : this.getNode(keyNodeEntryRawOrPredicate);
|
|
11047
11502
|
if (!nodeToDelete) {
|
|
11048
11503
|
return results;
|
|
11049
11504
|
}
|
|
11505
|
+
const willDeleteMin = nodeToDelete === this._minNode;
|
|
11506
|
+
const willDeleteMax = nodeToDelete === this._maxNode;
|
|
11507
|
+
const nextMin = willDeleteMin ? this._successorOf(nodeToDelete) : void 0;
|
|
11508
|
+
const nextMax = willDeleteMax ? this._predecessorOf(nodeToDelete) : void 0;
|
|
11050
11509
|
let originalColor = nodeToDelete.color;
|
|
11051
11510
|
let replacementNode;
|
|
11052
11511
|
if (!this.isRealNode(nodeToDelete.left)) {
|
|
@@ -11085,6 +11544,19 @@ var RedBlackTree = class extends BST {
|
|
|
11085
11544
|
}
|
|
11086
11545
|
if (this._isMapMode) this._store.delete(nodeToDelete.key);
|
|
11087
11546
|
this._size--;
|
|
11547
|
+
if (this._size <= 0) {
|
|
11548
|
+
this._setMinCache(void 0);
|
|
11549
|
+
this._setMaxCache(void 0);
|
|
11550
|
+
} else {
|
|
11551
|
+
if (willDeleteMin) this._setMinCache(nextMin);
|
|
11552
|
+
if (willDeleteMax) this._setMaxCache(nextMax);
|
|
11553
|
+
if (!this._minNode || !this.isRealNode(this._minNode)) {
|
|
11554
|
+
this._setMinCache(this.isRealNode(this._root) ? this.getLeftMost((n) => n, this._root) : void 0);
|
|
11555
|
+
}
|
|
11556
|
+
if (!this._maxNode || !this.isRealNode(this._maxNode)) {
|
|
11557
|
+
this._setMaxCache(this.isRealNode(this._root) ? this.getRightMost((n) => n, this._root) : void 0);
|
|
11558
|
+
}
|
|
11559
|
+
}
|
|
11088
11560
|
if (originalColor === "BLACK") {
|
|
11089
11561
|
this._deleteFixup(replacementNode);
|
|
11090
11562
|
}
|
|
@@ -11093,7 +11565,7 @@ var RedBlackTree = class extends BST {
|
|
|
11093
11565
|
}
|
|
11094
11566
|
/**
|
|
11095
11567
|
* Transform entries into a like-kind red-black tree with possibly different key/value types.
|
|
11096
|
-
* @remarks Time O(n), Space O(n)
|
|
11568
|
+
* @remarks Time O(n) average, Space O(n)
|
|
11097
11569
|
* @template MK
|
|
11098
11570
|
* @template MV
|
|
11099
11571
|
* @template MR
|
|
@@ -11110,40 +11582,61 @@ var RedBlackTree = class extends BST {
|
|
|
11110
11582
|
}
|
|
11111
11583
|
return out;
|
|
11112
11584
|
}
|
|
11585
|
+
/**
|
|
11586
|
+
* (Internal) Create an empty instance of the same concrete tree type.
|
|
11587
|
+
* @remarks Time O(1) average, Space O(1)
|
|
11588
|
+
*/
|
|
11113
11589
|
_createInstance(options) {
|
|
11114
11590
|
const Ctor = this.constructor;
|
|
11115
11591
|
return new Ctor([], { ...this._snapshotOptions(), ...options ?? {} });
|
|
11116
11592
|
}
|
|
11593
|
+
/**
|
|
11594
|
+
* (Internal) Create a like-kind tree (same concrete class) populated from an iterable.
|
|
11595
|
+
* @remarks Time O(m log m) average (m = iterable length), Space O(m)
|
|
11596
|
+
*/
|
|
11117
11597
|
_createLike(iter = [], options) {
|
|
11118
11598
|
const Ctor = this.constructor;
|
|
11119
11599
|
return new Ctor(iter, { ...this._snapshotOptions(), ...options ?? {} });
|
|
11120
11600
|
}
|
|
11601
|
+
/**
|
|
11602
|
+
* (Internal) Set the root pointer and keep header.parent in sync.
|
|
11603
|
+
* @remarks Time O(1), Space O(1)
|
|
11604
|
+
*/
|
|
11121
11605
|
_setRoot(v) {
|
|
11606
|
+
const NIL = this.NIL;
|
|
11122
11607
|
if (v) {
|
|
11123
11608
|
v.parent = void 0;
|
|
11124
11609
|
}
|
|
11125
11610
|
this._root = v;
|
|
11611
|
+
this._header.parent = v ?? NIL;
|
|
11126
11612
|
}
|
|
11613
|
+
/**
|
|
11614
|
+
* (Internal) Replace a node in place while preserving its color.
|
|
11615
|
+
* @remarks Time O(1) average, Space O(1)
|
|
11616
|
+
*/
|
|
11127
11617
|
_replaceNode(oldNode, newNode) {
|
|
11128
11618
|
newNode.color = oldNode.color;
|
|
11129
11619
|
return super._replaceNode(oldNode, newNode);
|
|
11130
11620
|
}
|
|
11131
11621
|
/**
|
|
11132
11622
|
* (Protected) Standard BST insert followed by red-black fix-up.
|
|
11133
|
-
* @remarks Time O(log n), Space O(1)
|
|
11623
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11134
11624
|
* @param node - Node to insert.
|
|
11135
11625
|
* @returns Status string: 'CREATED' or 'UPDATED'.
|
|
11136
11626
|
*/
|
|
11137
11627
|
_insert(node) {
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
|
|
11628
|
+
const NIL = this.NIL;
|
|
11629
|
+
const cmp = this._compare.bind(this);
|
|
11630
|
+
let current = this._header.parent ?? NIL;
|
|
11631
|
+
let parent;
|
|
11632
|
+
let lastCompared = 0;
|
|
11633
|
+
while (current !== NIL) {
|
|
11141
11634
|
parent = current;
|
|
11142
|
-
|
|
11143
|
-
if (
|
|
11144
|
-
current = current.left ??
|
|
11145
|
-
} else if (
|
|
11146
|
-
current = current.right ??
|
|
11635
|
+
lastCompared = cmp(node.key, current.key);
|
|
11636
|
+
if (lastCompared < 0) {
|
|
11637
|
+
current = current.left ?? NIL;
|
|
11638
|
+
} else if (lastCompared > 0) {
|
|
11639
|
+
current = current.right ?? NIL;
|
|
11147
11640
|
} else {
|
|
11148
11641
|
this._replaceNode(current, node);
|
|
11149
11642
|
return "UPDATED";
|
|
@@ -11152,13 +11645,13 @@ var RedBlackTree = class extends BST {
|
|
|
11152
11645
|
node.parent = parent;
|
|
11153
11646
|
if (!parent) {
|
|
11154
11647
|
this._setRoot(node);
|
|
11155
|
-
} else if (
|
|
11648
|
+
} else if (lastCompared < 0) {
|
|
11156
11649
|
parent.left = node;
|
|
11157
11650
|
} else {
|
|
11158
11651
|
parent.right = node;
|
|
11159
11652
|
}
|
|
11160
|
-
node.left =
|
|
11161
|
-
node.right =
|
|
11653
|
+
node.left = NIL;
|
|
11654
|
+
node.right = NIL;
|
|
11162
11655
|
node.color = "RED";
|
|
11163
11656
|
this._insertFixup(node);
|
|
11164
11657
|
return "CREATED";
|
|
@@ -11184,55 +11677,66 @@ var RedBlackTree = class extends BST {
|
|
|
11184
11677
|
}
|
|
11185
11678
|
/**
|
|
11186
11679
|
* (Protected) Restore red-black properties after insertion (recolor/rotate).
|
|
11187
|
-
* @remarks Time O(log n), Space O(1)
|
|
11680
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11188
11681
|
* @param z - Recently inserted node.
|
|
11189
11682
|
* @returns void
|
|
11190
11683
|
*/
|
|
11191
11684
|
_insertFixup(z) {
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11685
|
+
const leftRotate = this._leftRotate.bind(this);
|
|
11686
|
+
const rightRotate = this._rightRotate.bind(this);
|
|
11687
|
+
while (z) {
|
|
11688
|
+
const p = z.parent;
|
|
11689
|
+
if (!p || p.color !== "RED") break;
|
|
11690
|
+
const gp = p.parent;
|
|
11691
|
+
if (!gp) break;
|
|
11692
|
+
if (p === gp.left) {
|
|
11693
|
+
const y = gp.right;
|
|
11195
11694
|
if (y?.color === "RED") {
|
|
11196
|
-
|
|
11695
|
+
p.color = "BLACK";
|
|
11197
11696
|
y.color = "BLACK";
|
|
11198
|
-
|
|
11199
|
-
z =
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
|
|
11205
|
-
|
|
11206
|
-
|
|
11207
|
-
|
|
11208
|
-
|
|
11209
|
-
|
|
11697
|
+
gp.color = "RED";
|
|
11698
|
+
z = gp;
|
|
11699
|
+
continue;
|
|
11700
|
+
}
|
|
11701
|
+
if (z === p.right) {
|
|
11702
|
+
z = p;
|
|
11703
|
+
leftRotate(z);
|
|
11704
|
+
}
|
|
11705
|
+
const p2 = z?.parent;
|
|
11706
|
+
const gp2 = p2?.parent;
|
|
11707
|
+
if (p2 && gp2) {
|
|
11708
|
+
p2.color = "BLACK";
|
|
11709
|
+
gp2.color = "RED";
|
|
11710
|
+
rightRotate(gp2);
|
|
11210
11711
|
}
|
|
11211
11712
|
} else {
|
|
11212
|
-
const y =
|
|
11713
|
+
const y = gp.left;
|
|
11213
11714
|
if (y?.color === "RED") {
|
|
11214
|
-
|
|
11715
|
+
p.color = "BLACK";
|
|
11215
11716
|
y.color = "BLACK";
|
|
11216
|
-
|
|
11217
|
-
z =
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11717
|
+
gp.color = "RED";
|
|
11718
|
+
z = gp;
|
|
11719
|
+
continue;
|
|
11720
|
+
}
|
|
11721
|
+
if (z === p.left) {
|
|
11722
|
+
z = p;
|
|
11723
|
+
rightRotate(z);
|
|
11724
|
+
}
|
|
11725
|
+
const p2 = z?.parent;
|
|
11726
|
+
const gp2 = p2?.parent;
|
|
11727
|
+
if (p2 && gp2) {
|
|
11728
|
+
p2.color = "BLACK";
|
|
11729
|
+
gp2.color = "RED";
|
|
11730
|
+
leftRotate(gp2);
|
|
11228
11731
|
}
|
|
11229
11732
|
}
|
|
11733
|
+
break;
|
|
11230
11734
|
}
|
|
11231
11735
|
if (this.isRealNode(this._root)) this._root.color = "BLACK";
|
|
11232
11736
|
}
|
|
11233
11737
|
/**
|
|
11234
11738
|
* (Protected) Restore red-black properties after deletion (recolor/rotate).
|
|
11235
|
-
* @remarks Time O(log n), Space O(1)
|
|
11739
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
11236
11740
|
* @param node - Child that replaced the deleted node (may be undefined).
|
|
11237
11741
|
* @returns void
|
|
11238
11742
|
*/
|
|
@@ -12136,16 +12640,16 @@ var TreeCounter = class extends RedBlackTree {
|
|
|
12136
12640
|
/**
|
|
12137
12641
|
* Delete a node (or decrement its count) and rebalance if needed.
|
|
12138
12642
|
* @remarks Time O(log N), Space O(1)
|
|
12139
|
-
* @param
|
|
12643
|
+
* @param keyNodeEntryRawOrPredicate - Key, node, or [key, value] entry identifying the node.
|
|
12140
12644
|
* @param [ignoreCount] - If true, remove the node regardless of its count.
|
|
12141
12645
|
* @returns Array of deletion results including deleted node and a rebalance hint when present.
|
|
12142
12646
|
*/
|
|
12143
|
-
delete(
|
|
12144
|
-
if (
|
|
12647
|
+
delete(keyNodeEntryRawOrPredicate, ignoreCount = false) {
|
|
12648
|
+
if (keyNodeEntryRawOrPredicate === null) return [];
|
|
12145
12649
|
const results = [];
|
|
12146
12650
|
let nodeToDelete;
|
|
12147
|
-
if (this._isPredicate(
|
|
12148
|
-
else nodeToDelete = this.isRealNode(
|
|
12651
|
+
if (this._isPredicate(keyNodeEntryRawOrPredicate)) nodeToDelete = this.getNode(keyNodeEntryRawOrPredicate);
|
|
12652
|
+
else nodeToDelete = this.isRealNode(keyNodeEntryRawOrPredicate) ? keyNodeEntryRawOrPredicate : this.getNode(keyNodeEntryRawOrPredicate);
|
|
12149
12653
|
if (!nodeToDelete) {
|
|
12150
12654
|
return results;
|
|
12151
12655
|
}
|
|
@@ -12188,7 +12692,6 @@ var TreeCounter = class extends RedBlackTree {
|
|
|
12188
12692
|
if (ignoreCount || nodeToDelete.count <= 1) {
|
|
12189
12693
|
if (successor.right !== null) {
|
|
12190
12694
|
this._transplant(successor, successor.right);
|
|
12191
|
-
this._count -= nodeToDelete.count;
|
|
12192
12695
|
}
|
|
12193
12696
|
} else {
|
|
12194
12697
|
nodeToDelete.count--;
|
|
@@ -12291,6 +12794,11 @@ var TreeCounter = class extends RedBlackTree {
|
|
|
12291
12794
|
const out = this._createInstance();
|
|
12292
12795
|
this._clone(out);
|
|
12293
12796
|
out._count = this._count;
|
|
12797
|
+
for (const node of this.dfs((n) => n, "IN")) {
|
|
12798
|
+
if (!node) continue;
|
|
12799
|
+
const outNode = out.getNode(node.key);
|
|
12800
|
+
if (outNode) outNode.count = node.count;
|
|
12801
|
+
}
|
|
12294
12802
|
return out;
|
|
12295
12803
|
}
|
|
12296
12804
|
/**
|