red-black-tree-typed 2.4.0 → 2.4.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 +587 -93
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +593 -98
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +587 -93
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +593 -98
- 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 +6 -6
- package/dist/types/data-structures/binary-tree/bst.d.ts +2 -1
- package/dist/types/data-structures/binary-tree/index.d.ts +3 -3
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +150 -20
- package/dist/types/data-structures/binary-tree/tree-map.d.ts +188 -0
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +238 -147
- package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +270 -0
- package/dist/types/data-structures/binary-tree/tree-set.d.ts +181 -0
- package/dist/types/interfaces/binary-tree.d.ts +2 -2
- package/dist/types/types/data-structures/binary-tree/index.d.ts +3 -3
- package/dist/types/types/data-structures/binary-tree/tree-map.d.ts +33 -0
- package/dist/types/types/data-structures/binary-tree/tree-multi-set.d.ts +16 -0
- package/dist/types/types/data-structures/binary-tree/tree-set.d.ts +33 -0
- package/dist/umd/red-black-tree-typed.js +593 -98
- package/dist/umd/red-black-tree-typed.js.map +1 -1
- package/dist/umd/red-black-tree-typed.min.js +3 -3
- package/dist/umd/red-black-tree-typed.min.js.map +1 -1
- package/package.json +2 -2
- package/src/data-structures/base/linear-base.ts +2 -12
- package/src/data-structures/binary-tree/avl-tree.ts +1 -1
- package/src/data-structures/binary-tree/binary-tree.ts +45 -21
- package/src/data-structures/binary-tree/bst.ts +85 -10
- package/src/data-structures/binary-tree/index.ts +3 -3
- package/src/data-structures/binary-tree/red-black-tree.ts +568 -76
- package/src/data-structures/binary-tree/tree-map.ts +439 -0
- package/src/data-structures/binary-tree/tree-multi-map.ts +488 -325
- package/src/data-structures/binary-tree/tree-multi-set.ts +502 -0
- package/src/data-structures/binary-tree/tree-set.ts +407 -0
- package/src/data-structures/queue/deque.ts +10 -0
- package/src/data-structures/trie/trie.ts +6 -8
- package/src/interfaces/binary-tree.ts +2 -2
- package/src/types/data-structures/binary-tree/index.ts +3 -3
- package/src/types/data-structures/binary-tree/tree-map.ts +45 -0
- package/src/types/data-structures/binary-tree/tree-multi-set.ts +19 -0
- package/src/types/data-structures/binary-tree/tree-set.ts +39 -0
- package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +0 -236
- package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +0 -197
- package/dist/types/data-structures/binary-tree/tree-counter.d.ts +0 -243
- package/dist/types/types/data-structures/binary-tree/avl-tree-counter.d.ts +0 -2
- package/dist/types/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +0 -2
- package/dist/types/types/data-structures/binary-tree/tree-counter.d.ts +0 -2
- package/src/data-structures/binary-tree/avl-tree-counter.ts +0 -539
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +0 -438
- package/src/data-structures/binary-tree/tree-counter.ts +0 -575
- package/src/types/data-structures/binary-tree/avl-tree-counter.ts +0 -3
- package/src/types/data-structures/binary-tree/avl-tree-multi-map.ts +0 -3
- package/src/types/data-structures/binary-tree/tree-counter.ts +0 -3
package/dist/cjs/index.cjs
CHANGED
|
@@ -1231,6 +1231,9 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
1231
1231
|
get isDuplicate() {
|
|
1232
1232
|
return this._isDuplicate;
|
|
1233
1233
|
}
|
|
1234
|
+
// Map mode acceleration store:
|
|
1235
|
+
// - isMapMode=false: unused
|
|
1236
|
+
// - isMapMode=true: key -> node reference (O(1) has/getNode + fast get)
|
|
1234
1237
|
_store = /* @__PURE__ */ new Map();
|
|
1235
1238
|
/**
|
|
1236
1239
|
* Gets the external value store (used in Map mode).
|
|
@@ -1290,7 +1293,7 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
1290
1293
|
* @returns The newly created node.
|
|
1291
1294
|
*/
|
|
1292
1295
|
createNode(key, value) {
|
|
1293
|
-
return new BinaryTreeNode(key,
|
|
1296
|
+
return new BinaryTreeNode(key, value);
|
|
1294
1297
|
}
|
|
1295
1298
|
/**
|
|
1296
1299
|
* Creates a new, empty tree of the same type and configuration.
|
|
@@ -1437,11 +1440,11 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
1437
1440
|
* @returns True if the addition was successful, false otherwise.
|
|
1438
1441
|
*/
|
|
1439
1442
|
set(keyNodeOrEntry, value) {
|
|
1440
|
-
const [newNode
|
|
1443
|
+
const [newNode] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
|
1441
1444
|
if (newNode === void 0) return false;
|
|
1442
1445
|
if (!this._root) {
|
|
1443
1446
|
this._setRoot(newNode);
|
|
1444
|
-
if (this._isMapMode) this.
|
|
1447
|
+
if (this._isMapMode && newNode !== null && newNode !== void 0) this._store.set(newNode.key, newNode);
|
|
1445
1448
|
this._size = 1;
|
|
1446
1449
|
return true;
|
|
1447
1450
|
}
|
|
@@ -1453,7 +1456,7 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
1453
1456
|
if (!this._isDuplicate) {
|
|
1454
1457
|
if (newNode !== null && cur.key === newNode.key) {
|
|
1455
1458
|
this._replaceNode(cur, newNode);
|
|
1456
|
-
if (this._isMapMode) this.
|
|
1459
|
+
if (this._isMapMode && newNode !== null) this._store.set(cur.key, newNode);
|
|
1457
1460
|
return true;
|
|
1458
1461
|
}
|
|
1459
1462
|
}
|
|
@@ -1473,7 +1476,7 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
1473
1476
|
} else if (potentialParent.right === void 0) {
|
|
1474
1477
|
potentialParent.right = newNode;
|
|
1475
1478
|
}
|
|
1476
|
-
if (this._isMapMode) this.
|
|
1479
|
+
if (this._isMapMode && newNode !== null && newNode !== void 0) this._store.set(newNode.key, newNode);
|
|
1477
1480
|
this._size++;
|
|
1478
1481
|
return true;
|
|
1479
1482
|
}
|
|
@@ -1540,13 +1543,13 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
1540
1543
|
* Deletes a node from the tree.
|
|
1541
1544
|
* @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).
|
|
1542
1545
|
*
|
|
1543
|
-
* @param
|
|
1546
|
+
* @param keyNodeEntryRawOrPredicate - The node to delete.
|
|
1544
1547
|
* @returns An array containing deletion results (for compatibility with self-balancing trees).
|
|
1545
1548
|
*/
|
|
1546
|
-
delete(
|
|
1549
|
+
delete(keyNodeEntryRawOrPredicate) {
|
|
1547
1550
|
const deletedResult = [];
|
|
1548
1551
|
if (!this._root) return deletedResult;
|
|
1549
|
-
const curr = this.getNode(
|
|
1552
|
+
const curr = this.getNode(keyNodeEntryRawOrPredicate);
|
|
1550
1553
|
if (!curr) return deletedResult;
|
|
1551
1554
|
const parent = curr?.parent;
|
|
1552
1555
|
let needBalanced;
|
|
@@ -1558,6 +1561,10 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
1558
1561
|
if (leftSubTreeRightMost) {
|
|
1559
1562
|
const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent;
|
|
1560
1563
|
orgCurrent = this._swapProperties(curr, leftSubTreeRightMost);
|
|
1564
|
+
if (this._isMapMode) {
|
|
1565
|
+
this._store.set(curr.key, curr);
|
|
1566
|
+
this._store.set(leftSubTreeRightMost.key, leftSubTreeRightMost);
|
|
1567
|
+
}
|
|
1561
1568
|
if (parentOfLeftSubTreeMax) {
|
|
1562
1569
|
if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost)
|
|
1563
1570
|
parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left;
|
|
@@ -1641,6 +1648,13 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
1641
1648
|
* @returns The first matching node, or undefined if not found.
|
|
1642
1649
|
*/
|
|
1643
1650
|
getNode(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
|
|
1651
|
+
if (this._isMapMode && keyNodeEntryOrPredicate !== null && keyNodeEntryOrPredicate !== void 0) {
|
|
1652
|
+
if (!this._isPredicate(keyNodeEntryOrPredicate)) {
|
|
1653
|
+
const key = this._extractKey(keyNodeEntryOrPredicate);
|
|
1654
|
+
if (key === null || key === void 0) return;
|
|
1655
|
+
return this._store.get(key);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1644
1658
|
return this.search(keyNodeEntryOrPredicate, true, (node) => node, startNode, iterationType)[0];
|
|
1645
1659
|
}
|
|
1646
1660
|
/**
|
|
@@ -1656,11 +1670,18 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
1656
1670
|
if (this._isMapMode) {
|
|
1657
1671
|
const key = this._extractKey(keyNodeEntryOrPredicate);
|
|
1658
1672
|
if (key === null || key === void 0) return;
|
|
1659
|
-
return this._store.get(key);
|
|
1673
|
+
return this._store.get(key)?.value;
|
|
1660
1674
|
}
|
|
1661
1675
|
return this.getNode(keyNodeEntryOrPredicate, startNode, iterationType)?.value;
|
|
1662
1676
|
}
|
|
1663
1677
|
has(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
|
|
1678
|
+
if (this._isMapMode && keyNodeEntryOrPredicate !== void 0 && keyNodeEntryOrPredicate !== null) {
|
|
1679
|
+
if (!this._isPredicate(keyNodeEntryOrPredicate)) {
|
|
1680
|
+
const key = this._extractKey(keyNodeEntryOrPredicate);
|
|
1681
|
+
if (key === null || key === void 0) return false;
|
|
1682
|
+
return this._store.has(key);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1664
1685
|
return this.search(keyNodeEntryOrPredicate, true, (node) => node, startNode, iterationType).length > 0;
|
|
1665
1686
|
}
|
|
1666
1687
|
/**
|
|
@@ -1729,7 +1750,7 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
1729
1750
|
}
|
|
1730
1751
|
return true;
|
|
1731
1752
|
}, "checkBST");
|
|
1732
|
-
const isStandardBST = checkBST(
|
|
1753
|
+
const isStandardBST = checkBST();
|
|
1733
1754
|
const isInverseBST = checkBST(true);
|
|
1734
1755
|
return isStandardBST || isInverseBST;
|
|
1735
1756
|
}
|
|
@@ -2403,8 +2424,7 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
2403
2424
|
}
|
|
2404
2425
|
current = stack.pop();
|
|
2405
2426
|
if (this.isRealNode(current)) {
|
|
2406
|
-
|
|
2407
|
-
else yield [current.key, current.value];
|
|
2427
|
+
yield [current.key, current.value];
|
|
2408
2428
|
current = current.right;
|
|
2409
2429
|
}
|
|
2410
2430
|
}
|
|
@@ -2412,8 +2432,7 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
2412
2432
|
if (node.left && this.isRealNode(node)) {
|
|
2413
2433
|
yield* this[Symbol.iterator](node.left);
|
|
2414
2434
|
}
|
|
2415
|
-
|
|
2416
|
-
else yield [node.key, node.value];
|
|
2435
|
+
yield [node.key, node.value];
|
|
2417
2436
|
if (node.right && this.isRealNode(node)) {
|
|
2418
2437
|
yield* this[Symbol.iterator](node.right);
|
|
2419
2438
|
}
|
|
@@ -2499,8 +2518,7 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
2499
2518
|
(node) => {
|
|
2500
2519
|
if (node === null) cloned.set(null);
|
|
2501
2520
|
else {
|
|
2502
|
-
|
|
2503
|
-
else cloned.set([node.key, node.value]);
|
|
2521
|
+
cloned.set([node.key, node.value]);
|
|
2504
2522
|
}
|
|
2505
2523
|
},
|
|
2506
2524
|
this._root,
|
|
@@ -2508,7 +2526,6 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
2508
2526
|
true
|
|
2509
2527
|
// Include nulls
|
|
2510
2528
|
);
|
|
2511
|
-
if (this._isMapMode) cloned._store = this._store;
|
|
2512
2529
|
}
|
|
2513
2530
|
/**
|
|
2514
2531
|
* (Protected) Recursive helper for `toVisual`.
|
|
@@ -2671,8 +2688,10 @@ var BinaryTree = class extends IterableEntryBase {
|
|
|
2671
2688
|
*/
|
|
2672
2689
|
_setValue(key, value) {
|
|
2673
2690
|
if (key === null || key === void 0) return false;
|
|
2674
|
-
|
|
2675
|
-
|
|
2691
|
+
const node = this._store.get(key);
|
|
2692
|
+
if (!node) return false;
|
|
2693
|
+
node.value = value;
|
|
2694
|
+
return true;
|
|
2676
2695
|
}
|
|
2677
2696
|
/**
|
|
2678
2697
|
* (Protected) Clears all nodes from the tree.
|
|
@@ -2883,7 +2902,7 @@ var BST = class extends BinaryTree {
|
|
|
2883
2902
|
* @returns The newly created BSTNode.
|
|
2884
2903
|
*/
|
|
2885
2904
|
createNode(key, value) {
|
|
2886
|
-
return new BSTNode(key,
|
|
2905
|
+
return new BSTNode(key, value);
|
|
2887
2906
|
}
|
|
2888
2907
|
/**
|
|
2889
2908
|
* Ensures the input is a node. If it's a key or entry, it searches for the node.
|
|
@@ -2967,7 +2986,39 @@ var BST = class extends BinaryTree {
|
|
|
2967
2986
|
* @returns The first matching node, or undefined if not found.
|
|
2968
2987
|
*/
|
|
2969
2988
|
getNode(keyNodeEntryOrPredicate, startNode = this._root, iterationType = this.iterationType) {
|
|
2970
|
-
|
|
2989
|
+
if (keyNodeEntryOrPredicate === null || keyNodeEntryOrPredicate === void 0) return void 0;
|
|
2990
|
+
if (this._isPredicate(keyNodeEntryOrPredicate)) {
|
|
2991
|
+
return this.getNodes(keyNodeEntryOrPredicate, true, startNode, iterationType)[0] ?? void 0;
|
|
2992
|
+
}
|
|
2993
|
+
if (keyNodeEntryOrPredicate instanceof Range) {
|
|
2994
|
+
return this.getNodes(
|
|
2995
|
+
keyNodeEntryOrPredicate,
|
|
2996
|
+
true,
|
|
2997
|
+
startNode,
|
|
2998
|
+
iterationType
|
|
2999
|
+
)[0] ?? void 0;
|
|
3000
|
+
}
|
|
3001
|
+
let targetKey;
|
|
3002
|
+
if (this.isNode(keyNodeEntryOrPredicate)) {
|
|
3003
|
+
targetKey = keyNodeEntryOrPredicate.key;
|
|
3004
|
+
} else if (this.isEntry(keyNodeEntryOrPredicate)) {
|
|
3005
|
+
const k = keyNodeEntryOrPredicate[0];
|
|
3006
|
+
if (k === null || k === void 0) return void 0;
|
|
3007
|
+
targetKey = k;
|
|
3008
|
+
} else {
|
|
3009
|
+
targetKey = keyNodeEntryOrPredicate;
|
|
3010
|
+
}
|
|
3011
|
+
const start = this.ensureNode(startNode);
|
|
3012
|
+
if (!start) return void 0;
|
|
3013
|
+
const NIL = this._NIL;
|
|
3014
|
+
let cur = start;
|
|
3015
|
+
const cmpFn = this._comparator;
|
|
3016
|
+
while (cur && cur !== NIL) {
|
|
3017
|
+
const c = cmpFn(targetKey, cur.key);
|
|
3018
|
+
if (c === 0) return cur;
|
|
3019
|
+
cur = c < 0 ? cur._left : cur._right;
|
|
3020
|
+
}
|
|
3021
|
+
return void 0;
|
|
2971
3022
|
}
|
|
2972
3023
|
/**
|
|
2973
3024
|
* Searches the tree for nodes matching a predicate, key, or range.
|
|
@@ -2988,8 +3039,30 @@ var BST = class extends BinaryTree {
|
|
|
2988
3039
|
if (keyNodeEntryOrPredicate === null) return [];
|
|
2989
3040
|
startNode = this.ensureNode(startNode);
|
|
2990
3041
|
if (!startNode) return [];
|
|
2991
|
-
let predicate;
|
|
2992
3042
|
const isRange = this.isRange(keyNodeEntryOrPredicate);
|
|
3043
|
+
const isPred = !isRange && this._isPredicate(keyNodeEntryOrPredicate);
|
|
3044
|
+
if (!isRange && !isPred) {
|
|
3045
|
+
let targetKey;
|
|
3046
|
+
if (this.isNode(keyNodeEntryOrPredicate)) {
|
|
3047
|
+
targetKey = keyNodeEntryOrPredicate.key;
|
|
3048
|
+
} else if (this.isEntry(keyNodeEntryOrPredicate)) {
|
|
3049
|
+
const k = keyNodeEntryOrPredicate[0];
|
|
3050
|
+
if (k !== null && k !== void 0) targetKey = k;
|
|
3051
|
+
} else {
|
|
3052
|
+
targetKey = keyNodeEntryOrPredicate;
|
|
3053
|
+
}
|
|
3054
|
+
if (targetKey === void 0) return [];
|
|
3055
|
+
const NIL = this._NIL;
|
|
3056
|
+
const cmpFn = this._comparator;
|
|
3057
|
+
let cur = startNode;
|
|
3058
|
+
while (cur && cur !== NIL) {
|
|
3059
|
+
const c = cmpFn(targetKey, cur.key);
|
|
3060
|
+
if (c === 0) return [callback(cur)];
|
|
3061
|
+
cur = c < 0 ? cur._left : cur._right;
|
|
3062
|
+
}
|
|
3063
|
+
return [];
|
|
3064
|
+
}
|
|
3065
|
+
let predicate;
|
|
2993
3066
|
if (isRange) {
|
|
2994
3067
|
predicate = /* @__PURE__ */ __name((node) => {
|
|
2995
3068
|
if (!node) return false;
|
|
@@ -3068,11 +3141,11 @@ var BST = class extends BinaryTree {
|
|
|
3068
3141
|
* @returns True if the addition was successful, false otherwise.
|
|
3069
3142
|
*/
|
|
3070
3143
|
set(keyNodeOrEntry, value) {
|
|
3071
|
-
const [newNode
|
|
3144
|
+
const [newNode] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
|
3072
3145
|
if (newNode === void 0) return false;
|
|
3073
3146
|
if (this._root === void 0) {
|
|
3074
3147
|
this._setRoot(newNode);
|
|
3075
|
-
if (this._isMapMode) this.
|
|
3148
|
+
if (this._isMapMode && this.isRealNode(newNode)) this._store.set(newNode.key, newNode);
|
|
3076
3149
|
this._size++;
|
|
3077
3150
|
return true;
|
|
3078
3151
|
}
|
|
@@ -3080,12 +3153,12 @@ var BST = class extends BinaryTree {
|
|
|
3080
3153
|
while (current !== void 0) {
|
|
3081
3154
|
if (this._compare(current.key, newNode.key) === 0) {
|
|
3082
3155
|
this._replaceNode(current, newNode);
|
|
3083
|
-
if (this._isMapMode) this.
|
|
3156
|
+
if (this._isMapMode && this.isRealNode(newNode)) this._store.set(current.key, newNode);
|
|
3084
3157
|
return true;
|
|
3085
3158
|
} else if (this._compare(current.key, newNode.key) > 0) {
|
|
3086
3159
|
if (current.left === void 0) {
|
|
3087
3160
|
current.left = newNode;
|
|
3088
|
-
if (this._isMapMode) this.
|
|
3161
|
+
if (this._isMapMode && this.isRealNode(newNode)) this._store.set(newNode.key, newNode);
|
|
3089
3162
|
this._size++;
|
|
3090
3163
|
return true;
|
|
3091
3164
|
}
|
|
@@ -3093,7 +3166,7 @@ var BST = class extends BinaryTree {
|
|
|
3093
3166
|
} else {
|
|
3094
3167
|
if (current.right === void 0) {
|
|
3095
3168
|
current.right = newNode;
|
|
3096
|
-
if (this._isMapMode) this.
|
|
3169
|
+
if (this._isMapMode && this.isRealNode(newNode)) this._store.set(newNode.key, newNode);
|
|
3097
3170
|
this._size++;
|
|
3098
3171
|
return true;
|
|
3099
3172
|
}
|
|
@@ -3911,7 +3984,7 @@ var BST = class extends BinaryTree {
|
|
|
3911
3984
|
succ.left = node.left;
|
|
3912
3985
|
if (succ.left) succ.left.parent = succ;
|
|
3913
3986
|
}
|
|
3914
|
-
this._size = Math.max(0,
|
|
3987
|
+
this._size = Math.max(0, this._size - 1);
|
|
3915
3988
|
return true;
|
|
3916
3989
|
}
|
|
3917
3990
|
};
|
|
@@ -3925,12 +3998,11 @@ var RedBlackTreeNode = class {
|
|
|
3925
3998
|
value;
|
|
3926
3999
|
parent = void 0;
|
|
3927
4000
|
/**
|
|
3928
|
-
* Create a Red-Black Tree
|
|
3929
|
-
* @remarks Time O(
|
|
3930
|
-
* @param key -
|
|
3931
|
-
* @param [value]-
|
|
3932
|
-
* @param color -
|
|
3933
|
-
* @returns New RedBlackTree instance.
|
|
4001
|
+
* Create a Red-Black Tree node.
|
|
4002
|
+
* @remarks Time O(1), Space O(1)
|
|
4003
|
+
* @param key - Node key.
|
|
4004
|
+
* @param [value] - Node value (unused in map mode trees).
|
|
4005
|
+
* @param color - Node color.
|
|
3934
4006
|
*/
|
|
3935
4007
|
constructor(key, value, color = "BLACK") {
|
|
3936
4008
|
this.key = key;
|
|
@@ -4061,11 +4133,33 @@ var RedBlackTree = class extends BST {
|
|
|
4061
4133
|
constructor(keysNodesEntriesOrRaws = [], options) {
|
|
4062
4134
|
super([], options);
|
|
4063
4135
|
this._root = this.NIL;
|
|
4136
|
+
this._header = new RedBlackTreeNode(void 0, void 0, "BLACK");
|
|
4137
|
+
this._header.parent = this.NIL;
|
|
4138
|
+
this._header._left = this.NIL;
|
|
4139
|
+
this._header._right = this.NIL;
|
|
4064
4140
|
if (keysNodesEntriesOrRaws) {
|
|
4065
4141
|
this.setMany(keysNodesEntriesOrRaws);
|
|
4066
4142
|
}
|
|
4067
4143
|
}
|
|
4068
4144
|
_root;
|
|
4145
|
+
/**
|
|
4146
|
+
* (Internal) Header sentinel:
|
|
4147
|
+
* - header.parent -> root
|
|
4148
|
+
* - header._left -> min (or NIL)
|
|
4149
|
+
* - header._right -> max (or NIL)
|
|
4150
|
+
*
|
|
4151
|
+
* IMPORTANT:
|
|
4152
|
+
* - This header is NOT part of the actual tree.
|
|
4153
|
+
* - Do NOT use `header.left` / `header.right` accessors for wiring: those setters update `NIL.parent`
|
|
4154
|
+
* and can corrupt sentinel invariants / cause hangs. Only touch `header._left/_right`.
|
|
4155
|
+
*/
|
|
4156
|
+
_header;
|
|
4157
|
+
/**
|
|
4158
|
+
* (Internal) Cache of the current minimum and maximum nodes.
|
|
4159
|
+
* Used for fast-path insert/update when keys are monotonic or near-boundary.
|
|
4160
|
+
*/
|
|
4161
|
+
_minNode;
|
|
4162
|
+
_maxNode;
|
|
4069
4163
|
/**
|
|
4070
4164
|
* Get the current root node.
|
|
4071
4165
|
* @remarks Time O(1), Space O(1)
|
|
@@ -4083,7 +4177,7 @@ var RedBlackTree = class extends BST {
|
|
|
4083
4177
|
* @returns A new RedBlackTreeNode instance.
|
|
4084
4178
|
*/
|
|
4085
4179
|
createNode(key, value, color = "BLACK") {
|
|
4086
|
-
return new RedBlackTreeNode(key,
|
|
4180
|
+
return new RedBlackTreeNode(key, value, color);
|
|
4087
4181
|
}
|
|
4088
4182
|
/**
|
|
4089
4183
|
* Type guard: check whether the input is a RedBlackTreeNode.
|
|
@@ -4099,18 +4193,357 @@ var RedBlackTree = class extends BST {
|
|
|
4099
4193
|
* @remarks Time O(n), Space O(1)
|
|
4100
4194
|
* @returns void
|
|
4101
4195
|
*/
|
|
4196
|
+
/**
|
|
4197
|
+
* Remove all nodes and clear internal caches.
|
|
4198
|
+
* @remarks Time O(n) average, Space O(1)
|
|
4199
|
+
*/
|
|
4102
4200
|
clear() {
|
|
4103
4201
|
super.clear();
|
|
4104
4202
|
this._root = this.NIL;
|
|
4203
|
+
this._header.parent = this.NIL;
|
|
4204
|
+
this._setMinCache(void 0);
|
|
4205
|
+
this._setMaxCache(void 0);
|
|
4105
4206
|
}
|
|
4106
4207
|
/**
|
|
4107
|
-
*
|
|
4108
|
-
*
|
|
4109
|
-
*
|
|
4110
|
-
* @
|
|
4111
|
-
|
|
4208
|
+
* (Internal) Find a node by key using a tight BST walk (no allocations).
|
|
4209
|
+
*
|
|
4210
|
+
* NOTE: This uses `header.parent` as the canonical root pointer.
|
|
4211
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4212
|
+
*/
|
|
4213
|
+
_findNodeByKey(key) {
|
|
4214
|
+
const NIL = this.NIL;
|
|
4215
|
+
const cmp = this._compare.bind(this);
|
|
4216
|
+
let cur = this._header.parent ?? NIL;
|
|
4217
|
+
while (cur !== NIL) {
|
|
4218
|
+
const c = cmp(key, cur.key);
|
|
4219
|
+
if (c < 0) cur = cur.left ?? NIL;
|
|
4220
|
+
else if (c > 0) cur = cur.right ?? NIL;
|
|
4221
|
+
else return cur;
|
|
4222
|
+
}
|
|
4223
|
+
return void 0;
|
|
4224
|
+
}
|
|
4225
|
+
/**
|
|
4226
|
+
* (Internal) In-order predecessor of a node in a BST.
|
|
4227
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4228
|
+
*/
|
|
4229
|
+
_predecessorOf(node) {
|
|
4230
|
+
const NIL = this.NIL;
|
|
4231
|
+
if (node.left && node.left !== NIL) {
|
|
4232
|
+
let cur2 = node.left;
|
|
4233
|
+
while (cur2.right && cur2.right !== NIL) cur2 = cur2.right;
|
|
4234
|
+
return cur2;
|
|
4235
|
+
}
|
|
4236
|
+
let cur = node;
|
|
4237
|
+
let p = node.parent;
|
|
4238
|
+
while (p && cur === p.left) {
|
|
4239
|
+
cur = p;
|
|
4240
|
+
p = p.parent;
|
|
4241
|
+
}
|
|
4242
|
+
return p;
|
|
4243
|
+
}
|
|
4244
|
+
/**
|
|
4245
|
+
* (Internal) In-order successor of a node in a BST.
|
|
4246
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4247
|
+
*/
|
|
4248
|
+
_successorOf(node) {
|
|
4249
|
+
const NIL = this.NIL;
|
|
4250
|
+
if (node.right && node.right !== NIL) {
|
|
4251
|
+
let cur2 = node.right;
|
|
4252
|
+
while (cur2.left && cur2.left !== NIL) cur2 = cur2.left;
|
|
4253
|
+
return cur2;
|
|
4254
|
+
}
|
|
4255
|
+
let cur = node;
|
|
4256
|
+
let p = node.parent;
|
|
4257
|
+
while (p && cur === p.right) {
|
|
4258
|
+
cur = p;
|
|
4259
|
+
p = p.parent;
|
|
4260
|
+
}
|
|
4261
|
+
return p;
|
|
4262
|
+
}
|
|
4263
|
+
/**
|
|
4264
|
+
* (Internal) Attach a new node directly under a known parent/side (no search).
|
|
4265
|
+
*
|
|
4266
|
+
* This is a performance-oriented helper used by boundary fast paths and hinted insertion.
|
|
4267
|
+
* It will:
|
|
4268
|
+
* - wire parent/child pointers (using accessors, so parent pointers are updated)
|
|
4269
|
+
* - initialize children to NIL
|
|
4270
|
+
* - mark the new node RED, then run insert fix-up
|
|
4271
|
+
*
|
|
4272
|
+
* Precondition: the chosen slot (parent.left/parent.right) is empty (NIL/null/undefined).
|
|
4273
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4274
|
+
*/
|
|
4275
|
+
_attachNewNode(parent, side, node) {
|
|
4276
|
+
const NIL = this.NIL;
|
|
4277
|
+
node.parent = parent;
|
|
4278
|
+
if (side === "left") parent.left = node;
|
|
4279
|
+
else parent.right = node;
|
|
4280
|
+
node.left = NIL;
|
|
4281
|
+
node.right = NIL;
|
|
4282
|
+
node.color = "RED";
|
|
4283
|
+
this._insertFixup(node);
|
|
4284
|
+
if (this.isRealNode(this._root)) this._root.color = "BLACK";
|
|
4285
|
+
}
|
|
4286
|
+
/**
|
|
4287
|
+
* (Internal) a single source of truth for min/max is header._left/_right.
|
|
4288
|
+
* Keep legacy _minNode/_maxNode mirrored for compatibility.
|
|
4289
|
+
* @remarks Time O(1), Space O(1)
|
|
4290
|
+
*/
|
|
4291
|
+
/**
|
|
4292
|
+
* (Internal) Update min cache pointers (header._left is the canonical min pointer).
|
|
4293
|
+
* @remarks Time O(1), Space O(1)
|
|
4294
|
+
*/
|
|
4295
|
+
_setMinCache(node) {
|
|
4296
|
+
this._minNode = node;
|
|
4297
|
+
this._header._left = node ?? this.NIL;
|
|
4298
|
+
}
|
|
4299
|
+
/**
|
|
4300
|
+
* (Internal) Update max cache pointers (header._right is the canonical max pointer).
|
|
4301
|
+
* @remarks Time O(1), Space O(1)
|
|
4302
|
+
*/
|
|
4303
|
+
_setMaxCache(node) {
|
|
4304
|
+
this._maxNode = node;
|
|
4305
|
+
this._header._right = node ?? this.NIL;
|
|
4306
|
+
}
|
|
4307
|
+
/**
|
|
4308
|
+
* (Internal) Core set implementation returning the affected node.
|
|
4309
|
+
*
|
|
4310
|
+
* Hot path goals:
|
|
4311
|
+
* - Avoid double walks (search+insert): do a single traversal that either updates or inserts.
|
|
4312
|
+
* - Use header min/max caches to fast-path boundary inserts.
|
|
4313
|
+
* - Keep header._left/_right as canonical min/max pointers.
|
|
4314
|
+
*
|
|
4315
|
+
* Return value:
|
|
4316
|
+
* - `{ node, created:false }` when an existing key is updated
|
|
4317
|
+
* - `{ node, created:true }` when a new node is inserted
|
|
4318
|
+
* - `undefined` only on unexpected internal failure.
|
|
4319
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4320
|
+
*/
|
|
4321
|
+
_setKVNode(key, nextValue) {
|
|
4322
|
+
const NIL = this.NIL;
|
|
4323
|
+
const comparator = this._comparator;
|
|
4324
|
+
const header = this._header;
|
|
4325
|
+
const minN = header._left ?? NIL;
|
|
4326
|
+
if (minN !== NIL) {
|
|
4327
|
+
const cMin = comparator(key, minN.key);
|
|
4328
|
+
if (cMin === 0) {
|
|
4329
|
+
minN.value = nextValue;
|
|
4330
|
+
if (this._isMapMode) this._store.set(key, minN);
|
|
4331
|
+
return { node: minN, created: false };
|
|
4332
|
+
}
|
|
4333
|
+
const minL = minN.left;
|
|
4334
|
+
if (cMin < 0 && (minL === NIL || minL === null || minL === void 0)) {
|
|
4335
|
+
const newNode2 = this.createNode(key, nextValue);
|
|
4336
|
+
this._attachNewNode(minN, "left", newNode2);
|
|
4337
|
+
if (this._isMapMode) this._store.set(newNode2.key, newNode2);
|
|
4338
|
+
this._size++;
|
|
4339
|
+
this._setMinCache(newNode2);
|
|
4340
|
+
if (header._right === NIL) this._setMaxCache(newNode2);
|
|
4341
|
+
return { node: newNode2, created: true };
|
|
4342
|
+
}
|
|
4343
|
+
if (cMin > 0) {
|
|
4344
|
+
const maxN = header._right ?? NIL;
|
|
4345
|
+
const cMax = comparator(key, maxN.key);
|
|
4346
|
+
if (cMax === 0) {
|
|
4347
|
+
maxN.value = nextValue;
|
|
4348
|
+
if (this._isMapMode) this._store.set(key, maxN);
|
|
4349
|
+
return { node: maxN, created: false };
|
|
4350
|
+
}
|
|
4351
|
+
const maxR = maxN.right;
|
|
4352
|
+
if (cMax > 0 && (maxR === NIL || maxR === null || maxR === void 0)) {
|
|
4353
|
+
const newNode2 = this.createNode(key, nextValue);
|
|
4354
|
+
this._attachNewNode(maxN, "right", newNode2);
|
|
4355
|
+
if (this._isMapMode) this._store.set(newNode2.key, newNode2);
|
|
4356
|
+
this._size++;
|
|
4357
|
+
this._setMaxCache(newNode2);
|
|
4358
|
+
if (header._left === NIL) this._setMinCache(newNode2);
|
|
4359
|
+
return { node: newNode2, created: true };
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
}
|
|
4363
|
+
const cmp = comparator;
|
|
4364
|
+
const isMapMode = this._isMapMode;
|
|
4365
|
+
const store = this._store;
|
|
4366
|
+
let current = this._header.parent ?? NIL;
|
|
4367
|
+
let parent;
|
|
4368
|
+
let lastCompared = 0;
|
|
4369
|
+
while (current !== NIL) {
|
|
4370
|
+
parent = current;
|
|
4371
|
+
lastCompared = cmp(key, current.key);
|
|
4372
|
+
if (lastCompared < 0) current = current.left ?? NIL;
|
|
4373
|
+
else if (lastCompared > 0) current = current.right ?? NIL;
|
|
4374
|
+
else {
|
|
4375
|
+
current.value = nextValue;
|
|
4376
|
+
if (isMapMode) store.set(key, current);
|
|
4377
|
+
return { node: current, created: false };
|
|
4378
|
+
}
|
|
4379
|
+
}
|
|
4380
|
+
const newNode = this.createNode(key, nextValue);
|
|
4381
|
+
newNode.parent = parent;
|
|
4382
|
+
if (!parent) {
|
|
4383
|
+
this._setRoot(newNode);
|
|
4384
|
+
} else if (lastCompared < 0) {
|
|
4385
|
+
parent.left = newNode;
|
|
4386
|
+
} else {
|
|
4387
|
+
parent.right = newNode;
|
|
4388
|
+
}
|
|
4389
|
+
newNode.left = NIL;
|
|
4390
|
+
newNode.right = NIL;
|
|
4391
|
+
newNode.color = "RED";
|
|
4392
|
+
this._insertFixup(newNode);
|
|
4393
|
+
if (this.isRealNode(this._root)) this._root.color = "BLACK";
|
|
4394
|
+
else return void 0;
|
|
4395
|
+
if (isMapMode) store.set(newNode.key, newNode);
|
|
4396
|
+
this._size++;
|
|
4397
|
+
const hMin = this._header._left ?? NIL;
|
|
4398
|
+
const hMax = this._header._right ?? NIL;
|
|
4399
|
+
if (hMin === NIL || hMax === NIL) {
|
|
4400
|
+
this._setMinCache(newNode);
|
|
4401
|
+
this._setMaxCache(newNode);
|
|
4402
|
+
} else if (parent === hMax && lastCompared > 0) {
|
|
4403
|
+
this._setMaxCache(newNode);
|
|
4404
|
+
} else if (parent === hMin && lastCompared < 0) {
|
|
4405
|
+
this._setMinCache(newNode);
|
|
4406
|
+
} else {
|
|
4407
|
+
if (cmp(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
4408
|
+
if (cmp(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
4409
|
+
}
|
|
4410
|
+
return { node: newNode, created: true };
|
|
4411
|
+
}
|
|
4412
|
+
/**
|
|
4413
|
+
* (Internal) Boolean wrapper around `_setKVNode`.
|
|
4414
|
+
*
|
|
4415
|
+
* Includes a map-mode update fast-path:
|
|
4416
|
+
* - If `isMapMode=true` and the key already exists in `_store`, then updating the value does not
|
|
4417
|
+
* require any tree search/rotation (tree shape depends only on key).
|
|
4418
|
+
* - This path is intentionally limited to `nextValue !== undefined` to preserve existing
|
|
4419
|
+
* semantics for `undefined` values.
|
|
4420
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4421
|
+
*/
|
|
4422
|
+
_setKV(key, nextValue) {
|
|
4423
|
+
if (this._isMapMode) {
|
|
4424
|
+
const store = this._store;
|
|
4425
|
+
const node = store.get(key);
|
|
4426
|
+
if (node) {
|
|
4427
|
+
node.value = nextValue;
|
|
4428
|
+
return true;
|
|
4429
|
+
}
|
|
4430
|
+
}
|
|
4431
|
+
return this._setKVNode(key, nextValue) !== void 0;
|
|
4432
|
+
}
|
|
4433
|
+
/**
|
|
4434
|
+
* Insert/update using a hint node to speed up nearby insertions.
|
|
4435
|
+
*
|
|
4436
|
+
* close to the expected insertion position (often the previously returned node in a loop).
|
|
4437
|
+
*
|
|
4438
|
+
* When the hint is a good fit (sorted / nearly-sorted insertion), this can avoid most of the
|
|
4439
|
+
* normal root-to-leaf search and reduce constant factors.
|
|
4440
|
+
*
|
|
4441
|
+
* When the hint does not match (random workloads), this will fall back to the normal set path.
|
|
4442
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4443
|
+
*/
|
|
4444
|
+
setWithHintNode(key, value, hint) {
|
|
4445
|
+
if (!hint || !this.isRealNode(hint)) {
|
|
4446
|
+
return this._setKVNode(key, value)?.node;
|
|
4447
|
+
}
|
|
4448
|
+
const cmp = this._compare.bind(this);
|
|
4449
|
+
const c0 = cmp(key, hint.key);
|
|
4450
|
+
if (c0 === 0) {
|
|
4451
|
+
hint.value = value;
|
|
4452
|
+
if (this._isMapMode) this._store.set(key, hint);
|
|
4453
|
+
return hint;
|
|
4454
|
+
}
|
|
4455
|
+
if (c0 < 0) {
|
|
4456
|
+
if (!this.isRealNode(hint.left)) {
|
|
4457
|
+
const newNode = this.createNode(key, value);
|
|
4458
|
+
if (!this.isRealNode(newNode)) return void 0;
|
|
4459
|
+
this._attachNewNode(hint, "left", newNode);
|
|
4460
|
+
if (this._isMapMode) this._store.set(key, newNode);
|
|
4461
|
+
this._size++;
|
|
4462
|
+
const NIL = this.NIL;
|
|
4463
|
+
const hMin = this._header._left ?? NIL;
|
|
4464
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
4465
|
+
const hMax = this._header._right ?? NIL;
|
|
4466
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
4467
|
+
return newNode;
|
|
4468
|
+
}
|
|
4469
|
+
const pred = this._predecessorOf(hint);
|
|
4470
|
+
if (pred && cmp(pred.key, key) >= 0) {
|
|
4471
|
+
return this._setKVNode(key, value)?.node;
|
|
4472
|
+
}
|
|
4473
|
+
if (pred && !this.isRealNode(pred.right)) {
|
|
4474
|
+
const newNode = this.createNode(key, value);
|
|
4475
|
+
if (!this.isRealNode(newNode)) return void 0;
|
|
4476
|
+
this._attachNewNode(pred, "right", newNode);
|
|
4477
|
+
if (this._isMapMode) this._store.set(key, newNode);
|
|
4478
|
+
this._size++;
|
|
4479
|
+
const NIL = this.NIL;
|
|
4480
|
+
const hMin = this._header._left ?? NIL;
|
|
4481
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
4482
|
+
const hMax = this._header._right ?? NIL;
|
|
4483
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
4484
|
+
return newNode;
|
|
4485
|
+
}
|
|
4486
|
+
return this._setKVNode(key, value)?.node;
|
|
4487
|
+
}
|
|
4488
|
+
if (!this.isRealNode(hint.right)) {
|
|
4489
|
+
const newNode = this.createNode(key, value);
|
|
4490
|
+
if (!this.isRealNode(newNode)) return void 0;
|
|
4491
|
+
this._attachNewNode(hint, "right", newNode);
|
|
4492
|
+
if (this._isMapMode) this._store.set(key, newNode);
|
|
4493
|
+
this._size++;
|
|
4494
|
+
const NIL = this.NIL;
|
|
4495
|
+
const hMin = this._header._left ?? NIL;
|
|
4496
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
4497
|
+
const hMax = this._header._right ?? NIL;
|
|
4498
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
4499
|
+
return newNode;
|
|
4500
|
+
}
|
|
4501
|
+
const succ = this._successorOf(hint);
|
|
4502
|
+
if (succ && cmp(succ.key, key) <= 0) {
|
|
4503
|
+
return this._setKVNode(key, value)?.node;
|
|
4504
|
+
}
|
|
4505
|
+
if (succ && !this.isRealNode(succ.left)) {
|
|
4506
|
+
const newNode = this.createNode(key, value);
|
|
4507
|
+
if (!this.isRealNode(newNode)) return void 0;
|
|
4508
|
+
this._attachNewNode(succ, "left", newNode);
|
|
4509
|
+
if (this._isMapMode) this._store.set(key, newNode);
|
|
4510
|
+
this._size++;
|
|
4511
|
+
const NIL = this.NIL;
|
|
4512
|
+
const hMin = this._header._left ?? NIL;
|
|
4513
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
4514
|
+
const hMax = this._header._right ?? NIL;
|
|
4515
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
4516
|
+
return newNode;
|
|
4517
|
+
}
|
|
4518
|
+
return this._setKVNode(key, value)?.node;
|
|
4519
|
+
}
|
|
4520
|
+
/**
|
|
4521
|
+
* Boolean wrapper for setWithHintNode.
|
|
4522
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4523
|
+
*/
|
|
4524
|
+
setWithHint(key, value, hint) {
|
|
4525
|
+
return this.setWithHintNode(key, value, hint) !== void 0;
|
|
4526
|
+
}
|
|
4527
|
+
/**
|
|
4528
|
+
* Insert or update a key/value (map mode) or key-only (set mode).
|
|
4529
|
+
*
|
|
4530
|
+
* This method is optimized for:
|
|
4531
|
+
* - monotonic inserts via min/max boundary fast paths
|
|
4532
|
+
* - updates via a single-pass search (no double walk)
|
|
4533
|
+
*
|
|
4534
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4112
4535
|
*/
|
|
4113
4536
|
set(keyNodeOrEntry, value) {
|
|
4537
|
+
if (!this.isNode(keyNodeOrEntry)) {
|
|
4538
|
+
if (keyNodeOrEntry === null || keyNodeOrEntry === void 0) return false;
|
|
4539
|
+
if (this.isEntry(keyNodeOrEntry)) {
|
|
4540
|
+
const key = keyNodeOrEntry[0];
|
|
4541
|
+
if (key === null || key === void 0) return false;
|
|
4542
|
+
const nextValue = value ?? keyNodeOrEntry[1];
|
|
4543
|
+
return this._setKV(key, nextValue);
|
|
4544
|
+
}
|
|
4545
|
+
return this._setKV(keyNodeOrEntry, value);
|
|
4546
|
+
}
|
|
4114
4547
|
const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
|
4115
4548
|
if (!this.isRealNode(newNode)) return false;
|
|
4116
4549
|
const insertStatus = this._insert(newNode);
|
|
@@ -4120,31 +4553,47 @@ var RedBlackTree = class extends BST {
|
|
|
4120
4553
|
} else {
|
|
4121
4554
|
return false;
|
|
4122
4555
|
}
|
|
4123
|
-
if (this._isMapMode)
|
|
4556
|
+
if (this._isMapMode) {
|
|
4557
|
+
const n = this.getNode(newNode.key);
|
|
4558
|
+
if (this.isRealNode(n)) {
|
|
4559
|
+
n.value = newValue;
|
|
4560
|
+
this._store.set(n.key, n);
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
4124
4563
|
this._size++;
|
|
4125
4564
|
return true;
|
|
4126
4565
|
}
|
|
4127
4566
|
if (insertStatus === "UPDATED") {
|
|
4128
|
-
if (this._isMapMode)
|
|
4567
|
+
if (this._isMapMode) {
|
|
4568
|
+
const n = this.getNode(newNode.key);
|
|
4569
|
+
if (this.isRealNode(n)) {
|
|
4570
|
+
n.value = newValue;
|
|
4571
|
+
this._store.set(n.key, n);
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4129
4574
|
return true;
|
|
4130
4575
|
}
|
|
4131
4576
|
return false;
|
|
4132
4577
|
}
|
|
4133
4578
|
/**
|
|
4134
4579
|
* Delete a node by key/node/entry and rebalance as needed.
|
|
4135
|
-
* @remarks Time O(log n), Space O(1)
|
|
4136
|
-
* @param
|
|
4580
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4581
|
+
* @param keyNodeEntryRawOrPredicate - Key, node, or [key, value] entry identifying the node to delete.
|
|
4137
4582
|
* @returns Array with deletion metadata (removed node, rebalancing hint if any).
|
|
4138
4583
|
*/
|
|
4139
|
-
delete(
|
|
4140
|
-
if (
|
|
4584
|
+
delete(keyNodeEntryRawOrPredicate) {
|
|
4585
|
+
if (keyNodeEntryRawOrPredicate === null) return [];
|
|
4141
4586
|
const results = [];
|
|
4142
4587
|
let nodeToDelete;
|
|
4143
|
-
if (this._isPredicate(
|
|
4144
|
-
else nodeToDelete = this.isRealNode(
|
|
4588
|
+
if (this._isPredicate(keyNodeEntryRawOrPredicate)) nodeToDelete = this.getNode(keyNodeEntryRawOrPredicate);
|
|
4589
|
+
else nodeToDelete = this.isRealNode(keyNodeEntryRawOrPredicate) ? keyNodeEntryRawOrPredicate : this.getNode(keyNodeEntryRawOrPredicate);
|
|
4145
4590
|
if (!nodeToDelete) {
|
|
4146
4591
|
return results;
|
|
4147
4592
|
}
|
|
4593
|
+
const willDeleteMin = nodeToDelete === this._minNode;
|
|
4594
|
+
const willDeleteMax = nodeToDelete === this._maxNode;
|
|
4595
|
+
const nextMin = willDeleteMin ? this._successorOf(nodeToDelete) : void 0;
|
|
4596
|
+
const nextMax = willDeleteMax ? this._predecessorOf(nodeToDelete) : void 0;
|
|
4148
4597
|
let originalColor = nodeToDelete.color;
|
|
4149
4598
|
let replacementNode;
|
|
4150
4599
|
if (!this.isRealNode(nodeToDelete.left)) {
|
|
@@ -4183,6 +4632,19 @@ var RedBlackTree = class extends BST {
|
|
|
4183
4632
|
}
|
|
4184
4633
|
if (this._isMapMode) this._store.delete(nodeToDelete.key);
|
|
4185
4634
|
this._size--;
|
|
4635
|
+
if (this._size <= 0) {
|
|
4636
|
+
this._setMinCache(void 0);
|
|
4637
|
+
this._setMaxCache(void 0);
|
|
4638
|
+
} else {
|
|
4639
|
+
if (willDeleteMin) this._setMinCache(nextMin);
|
|
4640
|
+
if (willDeleteMax) this._setMaxCache(nextMax);
|
|
4641
|
+
if (!this._minNode || !this.isRealNode(this._minNode)) {
|
|
4642
|
+
this._setMinCache(this.isRealNode(this._root) ? this.getLeftMost((n) => n, this._root) : void 0);
|
|
4643
|
+
}
|
|
4644
|
+
if (!this._maxNode || !this.isRealNode(this._maxNode)) {
|
|
4645
|
+
this._setMaxCache(this.isRealNode(this._root) ? this.getRightMost((n) => n, this._root) : void 0);
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
4186
4648
|
if (originalColor === "BLACK") {
|
|
4187
4649
|
this._deleteFixup(replacementNode);
|
|
4188
4650
|
}
|
|
@@ -4191,7 +4653,7 @@ var RedBlackTree = class extends BST {
|
|
|
4191
4653
|
}
|
|
4192
4654
|
/**
|
|
4193
4655
|
* Transform entries into a like-kind red-black tree with possibly different key/value types.
|
|
4194
|
-
* @remarks Time O(n), Space O(n)
|
|
4656
|
+
* @remarks Time O(n) average, Space O(n)
|
|
4195
4657
|
* @template MK
|
|
4196
4658
|
* @template MV
|
|
4197
4659
|
* @template MR
|
|
@@ -4208,40 +4670,61 @@ var RedBlackTree = class extends BST {
|
|
|
4208
4670
|
}
|
|
4209
4671
|
return out;
|
|
4210
4672
|
}
|
|
4673
|
+
/**
|
|
4674
|
+
* (Internal) Create an empty instance of the same concrete tree type.
|
|
4675
|
+
* @remarks Time O(1) average, Space O(1)
|
|
4676
|
+
*/
|
|
4211
4677
|
_createInstance(options) {
|
|
4212
4678
|
const Ctor = this.constructor;
|
|
4213
4679
|
return new Ctor([], { ...this._snapshotOptions(), ...options ?? {} });
|
|
4214
4680
|
}
|
|
4681
|
+
/**
|
|
4682
|
+
* (Internal) Create a like-kind tree (same concrete class) populated from an iterable.
|
|
4683
|
+
* @remarks Time O(m log m) average (m = iterable length), Space O(m)
|
|
4684
|
+
*/
|
|
4215
4685
|
_createLike(iter = [], options) {
|
|
4216
4686
|
const Ctor = this.constructor;
|
|
4217
4687
|
return new Ctor(iter, { ...this._snapshotOptions(), ...options ?? {} });
|
|
4218
4688
|
}
|
|
4689
|
+
/**
|
|
4690
|
+
* (Internal) Set the root pointer and keep header.parent in sync.
|
|
4691
|
+
* @remarks Time O(1), Space O(1)
|
|
4692
|
+
*/
|
|
4219
4693
|
_setRoot(v) {
|
|
4694
|
+
const NIL = this.NIL;
|
|
4220
4695
|
if (v) {
|
|
4221
4696
|
v.parent = void 0;
|
|
4222
4697
|
}
|
|
4223
4698
|
this._root = v;
|
|
4699
|
+
this._header.parent = v ?? NIL;
|
|
4224
4700
|
}
|
|
4701
|
+
/**
|
|
4702
|
+
* (Internal) Replace a node in place while preserving its color.
|
|
4703
|
+
* @remarks Time O(1) average, Space O(1)
|
|
4704
|
+
*/
|
|
4225
4705
|
_replaceNode(oldNode, newNode) {
|
|
4226
4706
|
newNode.color = oldNode.color;
|
|
4227
4707
|
return super._replaceNode(oldNode, newNode);
|
|
4228
4708
|
}
|
|
4229
4709
|
/**
|
|
4230
4710
|
* (Protected) Standard BST insert followed by red-black fix-up.
|
|
4231
|
-
* @remarks Time O(log n), Space O(1)
|
|
4711
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4232
4712
|
* @param node - Node to insert.
|
|
4233
4713
|
* @returns Status string: 'CREATED' or 'UPDATED'.
|
|
4234
4714
|
*/
|
|
4235
4715
|
_insert(node) {
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4716
|
+
const NIL = this.NIL;
|
|
4717
|
+
const cmp = this._compare.bind(this);
|
|
4718
|
+
let current = this._header.parent ?? NIL;
|
|
4719
|
+
let parent;
|
|
4720
|
+
let lastCompared = 0;
|
|
4721
|
+
while (current !== NIL) {
|
|
4239
4722
|
parent = current;
|
|
4240
|
-
|
|
4241
|
-
if (
|
|
4242
|
-
current = current.left ??
|
|
4243
|
-
} else if (
|
|
4244
|
-
current = current.right ??
|
|
4723
|
+
lastCompared = cmp(node.key, current.key);
|
|
4724
|
+
if (lastCompared < 0) {
|
|
4725
|
+
current = current.left ?? NIL;
|
|
4726
|
+
} else if (lastCompared > 0) {
|
|
4727
|
+
current = current.right ?? NIL;
|
|
4245
4728
|
} else {
|
|
4246
4729
|
this._replaceNode(current, node);
|
|
4247
4730
|
return "UPDATED";
|
|
@@ -4250,13 +4733,13 @@ var RedBlackTree = class extends BST {
|
|
|
4250
4733
|
node.parent = parent;
|
|
4251
4734
|
if (!parent) {
|
|
4252
4735
|
this._setRoot(node);
|
|
4253
|
-
} else if (
|
|
4736
|
+
} else if (lastCompared < 0) {
|
|
4254
4737
|
parent.left = node;
|
|
4255
4738
|
} else {
|
|
4256
4739
|
parent.right = node;
|
|
4257
4740
|
}
|
|
4258
|
-
node.left =
|
|
4259
|
-
node.right =
|
|
4741
|
+
node.left = NIL;
|
|
4742
|
+
node.right = NIL;
|
|
4260
4743
|
node.color = "RED";
|
|
4261
4744
|
this._insertFixup(node);
|
|
4262
4745
|
return "CREATED";
|
|
@@ -4282,55 +4765,66 @@ var RedBlackTree = class extends BST {
|
|
|
4282
4765
|
}
|
|
4283
4766
|
/**
|
|
4284
4767
|
* (Protected) Restore red-black properties after insertion (recolor/rotate).
|
|
4285
|
-
* @remarks Time O(log n), Space O(1)
|
|
4768
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4286
4769
|
* @param z - Recently inserted node.
|
|
4287
4770
|
* @returns void
|
|
4288
4771
|
*/
|
|
4289
4772
|
_insertFixup(z) {
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4773
|
+
const leftRotate = this._leftRotate.bind(this);
|
|
4774
|
+
const rightRotate = this._rightRotate.bind(this);
|
|
4775
|
+
while (z) {
|
|
4776
|
+
const p = z.parent;
|
|
4777
|
+
if (!p || p.color !== "RED") break;
|
|
4778
|
+
const gp = p.parent;
|
|
4779
|
+
if (!gp) break;
|
|
4780
|
+
if (p === gp.left) {
|
|
4781
|
+
const y = gp.right;
|
|
4293
4782
|
if (y?.color === "RED") {
|
|
4294
|
-
|
|
4783
|
+
p.color = "BLACK";
|
|
4295
4784
|
y.color = "BLACK";
|
|
4296
|
-
|
|
4297
|
-
z =
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4785
|
+
gp.color = "RED";
|
|
4786
|
+
z = gp;
|
|
4787
|
+
continue;
|
|
4788
|
+
}
|
|
4789
|
+
if (z === p.right) {
|
|
4790
|
+
z = p;
|
|
4791
|
+
leftRotate(z);
|
|
4792
|
+
}
|
|
4793
|
+
const p2 = z?.parent;
|
|
4794
|
+
const gp2 = p2?.parent;
|
|
4795
|
+
if (p2 && gp2) {
|
|
4796
|
+
p2.color = "BLACK";
|
|
4797
|
+
gp2.color = "RED";
|
|
4798
|
+
rightRotate(gp2);
|
|
4308
4799
|
}
|
|
4309
4800
|
} else {
|
|
4310
|
-
const y =
|
|
4801
|
+
const y = gp.left;
|
|
4311
4802
|
if (y?.color === "RED") {
|
|
4312
|
-
|
|
4803
|
+
p.color = "BLACK";
|
|
4313
4804
|
y.color = "BLACK";
|
|
4314
|
-
|
|
4315
|
-
z =
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4805
|
+
gp.color = "RED";
|
|
4806
|
+
z = gp;
|
|
4807
|
+
continue;
|
|
4808
|
+
}
|
|
4809
|
+
if (z === p.left) {
|
|
4810
|
+
z = p;
|
|
4811
|
+
rightRotate(z);
|
|
4812
|
+
}
|
|
4813
|
+
const p2 = z?.parent;
|
|
4814
|
+
const gp2 = p2?.parent;
|
|
4815
|
+
if (p2 && gp2) {
|
|
4816
|
+
p2.color = "BLACK";
|
|
4817
|
+
gp2.color = "RED";
|
|
4818
|
+
leftRotate(gp2);
|
|
4326
4819
|
}
|
|
4327
4820
|
}
|
|
4821
|
+
break;
|
|
4328
4822
|
}
|
|
4329
4823
|
if (this.isRealNode(this._root)) this._root.color = "BLACK";
|
|
4330
4824
|
}
|
|
4331
4825
|
/**
|
|
4332
4826
|
* (Protected) Restore red-black properties after deletion (recolor/rotate).
|
|
4333
|
-
* @remarks Time O(log n), Space O(1)
|
|
4827
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
4334
4828
|
* @param node - Child that replaced the deleted node (may be undefined).
|
|
4335
4829
|
* @returns void
|
|
4336
4830
|
*/
|