data-structure-typed 2.5.3 → 2.6.1

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.
Files changed (158) hide show
  1. package/.github/workflows/ci.yml +7 -2
  2. package/.github/workflows/release-package.yml +9 -2
  3. package/.husky/pre-commit +3 -0
  4. package/CHANGELOG.md +1 -1
  5. package/MIGRATION.md +48 -0
  6. package/README.md +20 -2
  7. package/README_CN.md +20 -2
  8. package/SPECIFICATION.md +24 -0
  9. package/SPECIFICATION.zh-CN.md +24 -0
  10. package/dist/cjs/binary-tree.cjs +1897 -19
  11. package/dist/cjs/graph.cjs +174 -0
  12. package/dist/cjs/hash.cjs +33 -0
  13. package/dist/cjs/heap.cjs +71 -0
  14. package/dist/cjs/index.cjs +2383 -3
  15. package/dist/cjs/linked-list.cjs +224 -2
  16. package/dist/cjs/matrix.cjs +24 -0
  17. package/dist/cjs/priority-queue.cjs +71 -0
  18. package/dist/cjs/queue.cjs +221 -1
  19. package/dist/cjs/stack.cjs +59 -0
  20. package/dist/cjs/trie.cjs +62 -0
  21. package/dist/cjs-legacy/binary-tree.cjs +1897 -19
  22. package/dist/cjs-legacy/graph.cjs +174 -0
  23. package/dist/cjs-legacy/hash.cjs +33 -0
  24. package/dist/cjs-legacy/heap.cjs +71 -0
  25. package/dist/cjs-legacy/index.cjs +2383 -3
  26. package/dist/cjs-legacy/linked-list.cjs +224 -2
  27. package/dist/cjs-legacy/matrix.cjs +24 -0
  28. package/dist/cjs-legacy/priority-queue.cjs +71 -0
  29. package/dist/cjs-legacy/queue.cjs +221 -1
  30. package/dist/cjs-legacy/stack.cjs +59 -0
  31. package/dist/cjs-legacy/trie.cjs +62 -0
  32. package/dist/esm/binary-tree.mjs +1897 -19
  33. package/dist/esm/graph.mjs +174 -0
  34. package/dist/esm/hash.mjs +33 -0
  35. package/dist/esm/heap.mjs +71 -0
  36. package/dist/esm/index.mjs +2383 -3
  37. package/dist/esm/linked-list.mjs +224 -2
  38. package/dist/esm/matrix.mjs +24 -0
  39. package/dist/esm/priority-queue.mjs +71 -0
  40. package/dist/esm/queue.mjs +221 -1
  41. package/dist/esm/stack.mjs +59 -0
  42. package/dist/esm/trie.mjs +62 -0
  43. package/dist/esm-legacy/binary-tree.mjs +1897 -19
  44. package/dist/esm-legacy/graph.mjs +174 -0
  45. package/dist/esm-legacy/hash.mjs +33 -0
  46. package/dist/esm-legacy/heap.mjs +71 -0
  47. package/dist/esm-legacy/index.mjs +2383 -3
  48. package/dist/esm-legacy/linked-list.mjs +224 -2
  49. package/dist/esm-legacy/matrix.mjs +24 -0
  50. package/dist/esm-legacy/priority-queue.mjs +71 -0
  51. package/dist/esm-legacy/queue.mjs +221 -1
  52. package/dist/esm-legacy/stack.mjs +59 -0
  53. package/dist/esm-legacy/trie.mjs +62 -0
  54. package/dist/types/data-structures/base/iterable-element-base.d.ts +17 -0
  55. package/dist/types/data-structures/base/linear-base.d.ts +6 -0
  56. package/dist/types/data-structures/binary-tree/avl-tree.d.ts +36 -0
  57. package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +42 -0
  58. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +75 -0
  59. package/dist/types/data-structures/binary-tree/bst.d.ts +72 -0
  60. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +57 -0
  61. package/dist/types/data-structures/binary-tree/segment-tree.d.ts +18 -0
  62. package/dist/types/data-structures/binary-tree/tree-map.d.ts +375 -0
  63. package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +389 -0
  64. package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +330 -0
  65. package/dist/types/data-structures/binary-tree/tree-set.d.ts +438 -0
  66. package/dist/types/data-structures/graph/directed-graph.d.ts +30 -0
  67. package/dist/types/data-structures/graph/undirected-graph.d.ts +27 -0
  68. package/dist/types/data-structures/hash/hash-map.d.ts +33 -0
  69. package/dist/types/data-structures/heap/heap.d.ts +42 -0
  70. package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +75 -2
  71. package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +45 -0
  72. package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +54 -0
  73. package/dist/types/data-structures/matrix/matrix.d.ts +24 -0
  74. package/dist/types/data-structures/queue/deque.d.ts +90 -1
  75. package/dist/types/data-structures/queue/queue.d.ts +36 -0
  76. package/dist/types/data-structures/stack/stack.d.ts +30 -0
  77. package/dist/types/data-structures/trie/trie.d.ts +36 -0
  78. package/dist/umd/data-structure-typed.js +2383 -3
  79. package/dist/umd/data-structure-typed.min.js +3 -3
  80. package/docs-site-docusaurus/docs/api/classes/AVLTree.md +108 -108
  81. package/docs-site-docusaurus/docs/api/classes/BST.md +101 -101
  82. package/docs-site-docusaurus/docs/api/classes/BinaryIndexedTree.md +13 -13
  83. package/docs-site-docusaurus/docs/api/classes/BinaryTree.md +66 -66
  84. package/docs-site-docusaurus/docs/api/classes/Deque.md +235 -51
  85. package/docs-site-docusaurus/docs/api/classes/DirectedGraph.md +21 -21
  86. package/docs-site-docusaurus/docs/api/classes/DoublyLinkedList.md +231 -67
  87. package/docs-site-docusaurus/docs/api/classes/FibonacciHeap.md +9 -9
  88. package/docs-site-docusaurus/docs/api/classes/FibonacciHeapNode.md +1 -1
  89. package/docs-site-docusaurus/docs/api/classes/HashMap.md +14 -14
  90. package/docs-site-docusaurus/docs/api/classes/Heap.md +117 -34
  91. package/docs-site-docusaurus/docs/api/classes/IterableElementBase.md +83 -13
  92. package/docs-site-docusaurus/docs/api/classes/LinearBase.md +124 -20
  93. package/docs-site-docusaurus/docs/api/classes/LinearLinkedBase.md +140 -32
  94. package/docs-site-docusaurus/docs/api/classes/LinkedHashMap.md +30 -26
  95. package/docs-site-docusaurus/docs/api/classes/LinkedListQueue.md +159 -51
  96. package/docs-site-docusaurus/docs/api/classes/MapGraph.md +20 -20
  97. package/docs-site-docusaurus/docs/api/classes/Matrix.md +23 -23
  98. package/docs-site-docusaurus/docs/api/classes/MaxHeap.md +117 -34
  99. package/docs-site-docusaurus/docs/api/classes/MaxPriorityQueue.md +117 -34
  100. package/docs-site-docusaurus/docs/api/classes/MinHeap.md +117 -34
  101. package/docs-site-docusaurus/docs/api/classes/MinPriorityQueue.md +117 -34
  102. package/docs-site-docusaurus/docs/api/classes/PriorityQueue.md +117 -34
  103. package/docs-site-docusaurus/docs/api/classes/Queue.md +142 -34
  104. package/docs-site-docusaurus/docs/api/classes/RedBlackTree.md +117 -117
  105. package/docs-site-docusaurus/docs/api/classes/SegmentTree.md +8 -8
  106. package/docs-site-docusaurus/docs/api/classes/SinglyLinkedList.md +158 -50
  107. package/docs-site-docusaurus/docs/api/classes/SkipList.md +21 -21
  108. package/docs-site-docusaurus/docs/api/classes/Stack.md +108 -26
  109. package/docs-site-docusaurus/docs/api/classes/TreeMap.md +33 -33
  110. package/docs-site-docusaurus/docs/api/classes/TreeMultiMap.md +75 -39
  111. package/docs-site-docusaurus/docs/api/classes/TreeSet.md +301 -39
  112. package/docs-site-docusaurus/docs/api/classes/Trie.md +110 -28
  113. package/docs-site-docusaurus/docs/api/classes/UndirectedGraph.md +20 -20
  114. package/jest.integration.config.js +1 -2
  115. package/package.json +51 -50
  116. package/src/common/error.ts +15 -32
  117. package/src/common/index.ts +0 -3
  118. package/src/data-structures/base/iterable-element-base.ts +32 -3
  119. package/src/data-structures/base/linear-base.ts +13 -36
  120. package/src/data-structures/binary-tree/avl-tree.ts +31 -493
  121. package/src/data-structures/binary-tree/binary-indexed-tree.ts +47 -530
  122. package/src/data-structures/binary-tree/binary-tree.ts +326 -1236
  123. package/src/data-structures/binary-tree/bst.ts +158 -1010
  124. package/src/data-structures/binary-tree/red-black-tree.ts +451 -1233
  125. package/src/data-structures/binary-tree/segment-tree.ts +73 -333
  126. package/src/data-structures/binary-tree/tree-map.ts +462 -4749
  127. package/src/data-structures/binary-tree/tree-multi-map.ts +310 -4530
  128. package/src/data-structures/binary-tree/tree-multi-set.ts +300 -3652
  129. package/src/data-structures/binary-tree/tree-set.ts +437 -4443
  130. package/src/data-structures/graph/abstract-graph.ts +98 -167
  131. package/src/data-structures/graph/directed-graph.ts +137 -532
  132. package/src/data-structures/graph/map-graph.ts +0 -3
  133. package/src/data-structures/graph/undirected-graph.ts +132 -484
  134. package/src/data-structures/hash/hash-map.ts +154 -549
  135. package/src/data-structures/heap/heap.ts +200 -753
  136. package/src/data-structures/linked-list/doubly-linked-list.ts +153 -809
  137. package/src/data-structures/linked-list/singly-linked-list.ts +122 -749
  138. package/src/data-structures/linked-list/skip-linked-list.ts +211 -864
  139. package/src/data-structures/matrix/matrix.ts +179 -494
  140. package/src/data-structures/matrix/navigator.ts +0 -1
  141. package/src/data-structures/priority-queue/max-priority-queue.ts +1 -6
  142. package/src/data-structures/priority-queue/min-priority-queue.ts +6 -11
  143. package/src/data-structures/priority-queue/priority-queue.ts +1 -2
  144. package/src/data-structures/queue/deque.ts +241 -807
  145. package/src/data-structures/queue/queue.ts +102 -589
  146. package/src/data-structures/stack/stack.ts +76 -475
  147. package/src/data-structures/trie/trie.ts +98 -592
  148. package/src/types/common.ts +0 -10
  149. package/src/types/data-structures/binary-tree/bst.ts +0 -7
  150. package/src/types/data-structures/binary-tree/red-black-tree.ts +0 -1
  151. package/src/types/data-structures/graph/abstract-graph.ts +0 -2
  152. package/src/types/data-structures/hash/hash-map.ts +0 -3
  153. package/src/types/data-structures/hash/index.ts +0 -1
  154. package/src/types/data-structures/matrix/navigator.ts +0 -2
  155. package/src/types/utils/utils.ts +0 -7
  156. package/src/types/utils/validate-type.ts +0 -7
  157. package/src/utils/number.ts +0 -2
  158. package/src/utils/utils.ts +0 -5
@@ -5,15 +5,15 @@
5
5
  * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
6
6
  * @license MIT License
7
7
  */
8
-
9
8
  import type {
10
9
  BTNRep,
11
10
  CRUD,
12
11
  EntryCallback,
13
- FamilyPosition, NodePredicate,
12
+ FamilyPosition,
13
+ IterationType,
14
+ NodePredicate,
14
15
  OptNode,
15
16
  RBTNColor,
16
- IterationType,
17
17
  RedBlackTreeOptions
18
18
  } from '../../types';
19
19
  import { BST } from './bst';
@@ -31,7 +31,6 @@ export class RedBlackTreeNode<K = any, V = any> {
31
31
  * @param [value] - Node value (unused in map mode trees).
32
32
  * @param color - Node color.
33
33
  */
34
-
35
34
  constructor(key: K, value?: V, color: RBTNColor = 'BLACK') {
36
35
  this.key = key;
37
36
  this.value = value;
@@ -45,7 +44,6 @@ export class RedBlackTreeNode<K = any, V = any> {
45
44
  * @remarks Time O(1), Space O(1)
46
45
  * @returns Left child node, or null/undefined.
47
46
  */
48
-
49
47
  get left(): RedBlackTreeNode<K, V> | null | undefined {
50
48
  return this._left;
51
49
  }
@@ -56,7 +54,6 @@ export class RedBlackTreeNode<K = any, V = any> {
56
54
  * @param v - New left node, or null/undefined.
57
55
  * @returns void
58
56
  */
59
-
60
57
  set left(v: RedBlackTreeNode<K, V> | null | undefined) {
61
58
  if (v) {
62
59
  v.parent = this;
@@ -71,7 +68,6 @@ export class RedBlackTreeNode<K = any, V = any> {
71
68
  * @remarks Time O(1), Space O(1)
72
69
  * @returns Right child node, or null/undefined.
73
70
  */
74
-
75
71
  get right(): RedBlackTreeNode<K, V> | null | undefined {
76
72
  return this._right;
77
73
  }
@@ -82,7 +78,6 @@ export class RedBlackTreeNode<K = any, V = any> {
82
78
  * @param v - New right node, or null/undefined.
83
79
  * @returns void
84
80
  */
85
-
86
81
  set right(v: RedBlackTreeNode<K, V> | null | undefined) {
87
82
  if (v) {
88
83
  v.parent = this;
@@ -98,6 +93,7 @@ export class RedBlackTreeNode<K = any, V = any> {
98
93
  *
99
94
  * @returns The height.
100
95
  */
96
+
101
97
  /* istanbul ignore next -- covered by AVLTree tests (subclass uses height) */
102
98
  get height(): number {
103
99
  return this._height;
@@ -138,6 +134,7 @@ export class RedBlackTreeNode<K = any, V = any> {
138
134
  *
139
135
  * @returns The subtree node count.
140
136
  */
137
+
141
138
  /* istanbul ignore next -- internal field, exercised indirectly via tree operations */
142
139
  get count(): number {
143
140
  return this._count;
@@ -153,13 +150,11 @@ export class RedBlackTreeNode<K = any, V = any> {
153
150
  if (!this.parent) {
154
151
  return this.left || this.right ? 'ROOT' : 'ISOLATED';
155
152
  }
156
-
157
153
  if (this.parent.left === this) {
158
154
  return this.left || this.right ? 'ROOT_LEFT' : 'LEFT';
159
155
  } else if (this.parent.right === this) {
160
156
  return this.left || this.right ? 'ROOT_RIGHT' : 'RIGHT';
161
157
  }
162
-
163
158
  /* istanbul ignore next -- defensive: unreachable if tree structure is correct */
164
159
  return 'MAL_NODE';
165
160
  }
@@ -250,26 +245,41 @@ export class RedBlackTreeNode<K = any, V = any> {
250
245
  * console.log(stocksInRange.some((s: any) => s.symbol === 'META')); // true;
251
246
  * console.log(stocksInRange.some((s: any) => s.symbol === 'MSFT')); // true;
252
247
  */
253
-
254
248
  export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implements IBinaryTree<K, V, R> {
249
+ /**
250
+ * (Internal) Header sentinel:
251
+ * - header.parent -> root
252
+ * - header._left -> min (or NIL)
253
+ * - header._right -> max (or NIL)
254
+ *
255
+ * IMPORTANT:
256
+ * - This header is NOT part of the actual tree.
257
+ * - Do NOT use `header.left` / `header.right` accessors for wiring: those setters update `NIL.parent`
258
+ * and can corrupt sentinel invariants / cause hangs. Only touch `header._left/_right`.
259
+ */
260
+ protected _header: RedBlackTreeNode<K, V>;
261
+
262
+ /**
263
+ * (Internal) Cache of the current minimum and maximum nodes.
264
+ * Used for fast-path insert/update when keys are monotonic or near-boundary.
265
+ */
266
+ protected _minNode: RedBlackTreeNode<K, V> | undefined;
267
+ protected _maxNode: RedBlackTreeNode<K, V> | undefined;
268
+
255
269
  constructor(
256
270
  keysNodesEntriesOrRaws: Iterable<
257
271
  K | RedBlackTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | R
258
272
  > = [],
259
273
  options?: RedBlackTreeOptions<K, V, R>
260
274
  ) {
261
-
262
275
  super([], options);
263
-
264
276
  this._root = this.NIL;
265
-
266
277
  // Not part of the actual tree; used only as an internal cache hub.
267
278
  this._header = new RedBlackTreeNode<K, V>(undefined as K, undefined, 'BLACK');
268
279
  this._header.parent = this.NIL;
269
280
  // Avoid using accessors here: they would set NIL.parent and can corrupt sentinel invariants.
270
281
  this._header._left = this.NIL;
271
282
  this._header._right = this.NIL;
272
-
273
283
  if (keysNodesEntriesOrRaws) {
274
284
  this.setMany(keysNodesEntriesOrRaws);
275
285
  }
@@ -277,26 +287,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
277
287
 
278
288
  protected override _root: RedBlackTreeNode<K, V> | undefined;
279
289
 
280
- /**
281
- * (Internal) Header sentinel:
282
- * - header.parent -> root
283
- * - header._left -> min (or NIL)
284
- * - header._right -> max (or NIL)
285
- *
286
- * IMPORTANT:
287
- * - This header is NOT part of the actual tree.
288
- * - Do NOT use `header.left` / `header.right` accessors for wiring: those setters update `NIL.parent`
289
- * and can corrupt sentinel invariants / cause hangs. Only touch `header._left/_right`.
290
- */
291
- protected _header: RedBlackTreeNode<K, V>;
292
-
293
- /**
294
- * (Internal) Cache of the current minimum and maximum nodes.
295
- * Used for fast-path insert/update when keys are monotonic or near-boundary.
296
- */
297
- protected _minNode: RedBlackTreeNode<K, V> | undefined;
298
- protected _maxNode: RedBlackTreeNode<K, V> | undefined;
299
-
300
290
  /**
301
291
  * Get the current root node.
302
292
  * @remarks Time O(1), Space O(1)
@@ -324,7 +314,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
324
314
  * @param keyNodeOrEntry - See parameter type for details.
325
315
  * @returns True if the value is a RedBlackTreeNode.
326
316
  */
327
-
328
317
  override isNode(
329
318
  keyNodeOrEntry: K | RedBlackTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined
330
319
  ): keyNodeOrEntry is RedBlackTreeNode<K, V> {
@@ -334,158 +323,11 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
334
323
  /**
335
324
  * Remove all nodes, clear the key→value store (if in map mode) and internal caches.
336
325
  * @remarks Time O(n), Space O(1)
337
-
338
-
339
-
340
-
341
-
342
-
343
-
344
-
345
-
346
-
347
-
348
-
349
-
350
-
351
-
352
-
353
-
354
-
355
-
356
-
357
-
358
-
359
-
360
-
361
-
362
-
363
-
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
-
373
-
374
-
375
-
376
-
377
-
378
-
379
-
380
-
381
-
382
-
383
-
384
-
385
-
386
-
387
-
388
-
389
-
390
-
391
-
392
-
393
-
394
-
395
-
396
-
397
-
398
-
399
-
400
-
401
-
402
-
403
-
404
-
405
-
406
-
407
-
408
-
409
-
410
-
411
-
412
-
413
-
414
-
415
-
416
-
417
-
418
-
419
-
420
-
421
-
422
-
423
-
424
-
425
-
426
-
427
-
428
-
429
-
430
-
431
-
432
-
433
-
434
-
435
-
436
-
437
-
438
-
439
-
440
-
441
-
442
-
443
-
444
-
445
-
446
-
447
-
448
-
449
-
450
-
451
-
452
-
453
-
454
-
455
-
456
-
457
-
458
-
459
-
460
-
461
-
462
-
463
-
464
-
465
-
466
-
467
-
468
-
469
-
470
-
471
-
472
-
473
-
474
-
475
-
476
-
477
-
478
-
479
-
480
-
481
-
482
-
483
-
484
- * @example
485
- * // Remove all entries
486
- * const rbt = new RedBlackTree<number>([1, 2, 3]);
487
- * rbt.clear();
488
- * console.log(rbt.isEmpty()); // true;
326
+ * @example
327
+ * // Remove all entries
328
+ * const rbt = new RedBlackTree<number>([1, 2, 3]);
329
+ * rbt.clear();
330
+ * console.log(rbt.isEmpty()); // true;
489
331
  */
490
332
  override clear() {
491
333
  super.clear();
@@ -495,557 +337,137 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
495
337
  this._setMaxCache(undefined);
496
338
  }
497
339
 
498
-
499
340
  /**
500
- * (Internal) Find a node by key using a tight BST walk (no allocations).
341
+ * Insert/update using a hint node to speed up nearby insertions.
501
342
  *
502
- * NOTE: This uses `header.parent` as the canonical root pointer.
343
+ * close to the expected insertion position (often the previously returned node in a loop).
344
+ *
345
+ * When the hint is a good fit (sorted / nearly-sorted insertion), this can avoid most of the
346
+ * normal root-to-leaf search and reduce constant factors.
347
+ *
348
+ * When the hint does not match (random workloads), this will fall back to the normal set path.
503
349
  * @remarks Time O(log n) average, Space O(1)
504
350
  */
505
- protected _findNodeByKey(key: K): RedBlackTreeNode<K, V> | undefined {
506
- const NIL = this.NIL;
351
+ setWithHintNode(key: K, value: V, hint?: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
352
+ if (!hint || !this.isRealNode(hint)) {
353
+ return this._setKVNode(key, value)?.node;
354
+ }
507
355
  const cmp = this._compare.bind(this);
508
-
509
- let cur = (this._header.parent) ?? NIL;
510
- while (cur !== NIL) {
511
- const c = cmp(key, cur.key);
512
- if (c < 0) cur = cur.left ?? NIL;
513
- else if (c > 0) cur = cur.right ?? NIL;
514
- else return cur;
356
+ const c0 = cmp(key, hint.key);
357
+ if (c0 === 0) {
358
+ hint.value = value;
359
+ if (this._isMapMode) this._store.set(key, hint);
360
+ return hint;
515
361
  }
516
- return undefined;
517
- }
518
-
519
- /**
520
- * (Internal) In-order predecessor of a node in a BST.
521
- * @remarks Time O(log n) average, Space O(1)
522
- */
523
- protected _predecessorOf(node: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
524
- const NIL = this.NIL;
525
- if (node.left && node.left !== NIL) {
526
- let cur = node.left;
527
- while (cur.right && cur.right !== NIL) cur = cur.right;
528
- return cur;
362
+ if (c0 < 0) {
363
+ // Ultra-fast path: direct attach if the target slot is empty.
364
+ if (!this.isRealNode(hint.left)) {
365
+ const newNode = this.createNode(key, value);
366
+ if (!this.isRealNode(newNode)) return undefined;
367
+ this._attachNewNode(hint, 'left', newNode);
368
+ if (this._isMapMode) this._store.set(key, newNode);
369
+ this._size++;
370
+ // Maintain header/min/max caches.
371
+ const NIL = this.NIL;
372
+ const hMin = this._header._left ?? NIL;
373
+ if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
374
+ const hMax = this._header._right ?? NIL;
375
+ if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
376
+ return newNode;
377
+ }
378
+ const pred = this._predecessorOf(hint);
379
+ if (pred && cmp(pred.key, key) >= 0) {
380
+ return this._setKVNode(key, value)?.node;
381
+ }
382
+ // Try attach as right of pred.
383
+ if (pred && !this.isRealNode(pred.right)) {
384
+ const newNode = this.createNode(key, value);
385
+ if (!this.isRealNode(newNode)) return undefined;
386
+ this._attachNewNode(pred, 'right', newNode);
387
+ if (this._isMapMode) this._store.set(key, newNode);
388
+ this._size++;
389
+ // Maintain header/min/max caches.
390
+ const NIL = this.NIL;
391
+ const hMin = this._header._left ?? NIL;
392
+ if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
393
+ const hMax = this._header._right ?? NIL;
394
+ if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
395
+ return newNode;
396
+ }
397
+ /* istanbul ignore next -- structurally unreachable: predecessor never has a right child (it's the max of left subtree) */
398
+ return this._setKVNode(key, value)?.node;
529
399
  }
530
- let cur: RedBlackTreeNode<K, V> | undefined = node;
531
- let p = node.parent;
532
- while (p && cur === p.left) {
533
- cur = p;
534
- p = p.parent;
400
+ // c0 > 0
401
+ // Ultra-fast path: direct attach if the target slot is empty.
402
+ if (!this.isRealNode(hint.right)) {
403
+ const newNode = this.createNode(key, value);
404
+ if (!this.isRealNode(newNode)) return undefined;
405
+ this._attachNewNode(hint, 'right', newNode);
406
+ if (this._isMapMode) this._store.set(key, newNode);
407
+ this._size++;
408
+ // Maintain header/min/max caches.
409
+ const NIL = this.NIL;
410
+ const hMin = this._header._left ?? NIL;
411
+ if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
412
+ const hMax = this._header._right ?? NIL;
413
+ if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
414
+ return newNode;
535
415
  }
536
- return p;
537
- }
538
-
539
- /**
540
- * (Internal) In-order successor of a node in a BST.
541
- * @remarks Time O(log n) average, Space O(1)
542
- */
543
- protected _successorOf(node: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
544
- const NIL = this.NIL;
545
- if (node.right && node.right !== NIL) {
546
- let cur = node.right;
547
- while (cur.left && cur.left !== NIL) cur = cur.left;
548
- return cur;
416
+ const succ = this._successorOf(hint);
417
+ if (succ && cmp(succ.key, key) <= 0) {
418
+ return this._setKVNode(key, value)?.node;
549
419
  }
550
- let cur: RedBlackTreeNode<K, V> | undefined = node;
551
- let p = node.parent;
552
- while (p && cur === p.right) {
553
- cur = p;
554
- p = p.parent;
420
+ if (succ && !this.isRealNode(succ.left)) {
421
+ const newNode = this.createNode(key, value);
422
+ if (!this.isRealNode(newNode)) return undefined;
423
+ this._attachNewNode(succ, 'left', newNode);
424
+ if (this._isMapMode) this._store.set(key, newNode);
425
+ this._size++;
426
+ // Maintain header/min/max caches.
427
+ const NIL = this.NIL;
428
+ const hMin = this._header._left ?? NIL;
429
+ if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
430
+ const hMax = this._header._right ?? NIL;
431
+ if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
432
+ return newNode;
555
433
  }
556
- return p;
434
+ /* istanbul ignore next -- structurally unreachable: successor never has a left child (it's the min of right subtree) */
435
+ return this._setKVNode(key, value)?.node;
557
436
  }
558
437
 
559
438
  /**
560
- * (Internal) Attach a new node directly under a known parent/side (no search).
561
- *
562
- * This is a performance-oriented helper used by boundary fast paths and hinted insertion.
563
- * It will:
564
- * - wire parent/child pointers (using accessors, so parent pointers are updated)
565
- * - initialize children to NIL
566
- * - mark the new node RED, then run insert fix-up
567
- *
568
- * Precondition: the chosen slot (parent.left/parent.right) is empty (NIL/null/undefined).
439
+ * Boolean wrapper for setWithHintNode.
569
440
  * @remarks Time O(log n) average, Space O(1)
570
441
  */
571
- protected _attachNewNode(parent: RedBlackTreeNode<K, V>, side: 'left' | 'right', node: RedBlackTreeNode<K, V>): void {
572
- const NIL = this.NIL;
573
- node.parent = parent;
574
- if (side === 'left') parent.left = node;
575
- else parent.right = node;
576
- node.left = NIL;
577
- node.right = NIL;
578
- node.color = 'RED';
579
- this._updateCountAlongPath(node);
580
- this._insertFixup(node);
581
- if (this.isRealNode(this._root)) this._root.color = 'BLACK';
582
- }
583
-
584
- /**
585
- * (Internal) a single source of truth for min/max is header._left/_right.
586
- * Keep legacy _minNode/_maxNode mirrored for compatibility.
587
- * @remarks Time O(1), Space O(1)
588
- */
589
- /**
590
- * (Internal) Update min cache pointers (header._left is the canonical min pointer).
591
- * @remarks Time O(1), Space O(1)
592
- */
593
- protected _setMinCache(node: RedBlackTreeNode<K, V> | undefined): void {
594
- this._minNode = node;
595
- this._header._left = node ?? this.NIL;
596
- }
597
-
598
- /**
599
- * (Internal) Update max cache pointers (header._right is the canonical max pointer).
600
- * @remarks Time O(1), Space O(1)
601
- */
602
- protected _setMaxCache(node: RedBlackTreeNode<K, V> | undefined): void {
603
- this._maxNode = node;
604
- this._header._right = node ?? this.NIL;
442
+ setWithHint(key: K, value: V, hint?: RedBlackTreeNode<K, V>): boolean {
443
+ return this.setWithHintNode(key, value, hint) !== undefined;
605
444
  }
606
445
 
607
446
  /**
608
- * (Internal) Core set implementation returning the affected node.
447
+ * Insert or update a key/value (map mode) or key-only (set mode).
609
448
  *
610
- * Hot path goals:
611
- * - Avoid double walks (search+insert): do a single traversal that either updates or inserts.
612
- * - Use header min/max caches to fast-path boundary inserts.
613
- * - Keep header._left/_right as canonical min/max pointers.
449
+ * This method is optimized for:
450
+ * - monotonic inserts via min/max boundary fast paths
451
+ * - updates via a single-pass search (no double walk)
614
452
  *
615
- * Return value:
616
- * - `{ node, created:false }` when an existing key is updated
617
- * - `{ node, created:true }` when a new node is inserted
618
- * - `undefined` only on unexpected internal failure.
619
453
  * @remarks Time O(log n) average, Space O(1)
620
- */
621
- protected _setKVNode(key: K, nextValue?: V): { node: RedBlackTreeNode<K, V>; created: boolean } | undefined {
622
- const NIL = this.NIL;
623
- const comparator = this._comparator;
624
-
625
- // Read via header to avoid undefined checks (header uses NIL when empty).
626
- const header = this._header;
627
- const minN = header._left ?? NIL;
628
- if (minN !== NIL) {
629
- const cMin = comparator(key, minN.key);
630
- if (cMin === 0) {
631
- minN.value = nextValue as V;
632
- if (this._isMapMode) this._store.set(key, minN);
633
- return { node: minN, created: false };
634
- }
635
- // Boundary attach: if key is smaller than current min and min has no left child.
636
- // Inline NIL/null/undefined check to avoid isRealNode overhead on hot path.
637
- const minL = minN.left;
638
- if (cMin < 0 && (minL === NIL || minL === null || minL === undefined)) {
639
- const newNode = this.createNode(key, nextValue);
640
- this._attachNewNode(minN, 'left', newNode);
641
- if (this._isMapMode) this._store.set(newNode.key, newNode);
642
- this._size++;
643
- this._setMinCache(newNode);
644
- // If max is not initialized yet (tree had 0/1 nodes), mirror max too.
645
- if (header._right === NIL) this._setMaxCache(newNode);
646
- return { node: newNode, created: true };
647
- }
648
-
649
- // Only touch max when key is not less than min.
650
- if (cMin > 0) {
651
- const maxN = header._right ?? NIL;
652
- // Boundary attach: if key is greater than current max and max has no right child.
653
- const cMax = comparator(key, maxN.key);
654
- if (cMax === 0) {
655
- maxN.value = nextValue as V;
656
- if (this._isMapMode) this._store.set(key, maxN);
657
- return { node: maxN, created: false };
658
- }
659
- const maxR = maxN.right;
660
- if (cMax > 0 && (maxR === NIL || maxR === null || maxR === undefined)) {
661
- const newNode = this.createNode(key, nextValue);
662
- this._attachNewNode(maxN, 'right', newNode);
663
- if (this._isMapMode) this._store.set(newNode.key, newNode);
664
- this._size++;
665
- this._setMaxCache(newNode);
666
- if (header._left === NIL) this._setMinCache(newNode);
667
- return { node: newNode, created: true };
668
- }
669
- }
670
- }
671
-
672
- // Normal path: single-pass search + insert/update (avoid double-walking the tree).
673
- const cmp = comparator;
674
- const isMapMode = this._isMapMode;
675
- const store = this._store;
676
- let current = this._header.parent ?? NIL;
677
- let parent: RedBlackTreeNode<K, V> | undefined;
678
- let lastCompared = 0;
679
-
680
- while (current !== NIL) {
681
- parent = current;
682
- lastCompared = cmp(key, current.key);
683
- if (lastCompared < 0) current = current.left ?? NIL;
684
- else if (lastCompared > 0) current = current.right ?? NIL;
685
- else {
686
- // Update existing.
687
- current.value = nextValue as V;
688
- if (isMapMode) store.set(key, current);
689
- return { node: current, created: false };
690
- }
691
- }
692
-
693
- // Insert new.
694
- const newNode = this.createNode(key, nextValue);
695
- // createNode always returns a real node in RedBlackTree.
696
- newNode.parent = parent;
697
-
698
- if (!parent) {
699
- this._setRoot(newNode);
700
- } else if (lastCompared < 0) {
701
- parent.left = newNode;
702
- } else {
703
- parent.right = newNode;
704
- }
705
-
706
- newNode.left = NIL;
707
- newNode.right = NIL;
708
- newNode.color = 'RED';
709
-
710
- this._updateCountAlongPath(newNode);
711
- this._insertFixup(newNode);
712
- if (this.isRealNode(this._root)) this._root.color = 'BLACK';
713
- else return undefined;
714
-
715
- if (isMapMode) store.set(newNode.key, newNode);
716
- this._size++;
717
-
718
- // Maintain min/max caches on insertion (header.left/right are canonical).
719
- const hMin = this._header._left ?? NIL;
720
- const hMax = this._header._right ?? NIL;
721
-
722
- // Fast-path: empty tree or attaching directly to an extreme.
723
- if (hMin === NIL || hMax === NIL) {
724
- this._setMinCache(newNode);
725
- this._setMaxCache(newNode);
726
- } else /* istanbul ignore next -- boundary fast-paths at top of _setKVNode intercept boundary inserts before normal path */ if (parent === hMax && lastCompared > 0) {
727
- /* istanbul ignore next */ this._setMaxCache(newNode);
728
- } else /* istanbul ignore next */ if (parent === hMin && lastCompared < 0) {
729
- /* istanbul ignore next */ this._setMinCache(newNode);
730
- } else {
731
- if (cmp(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
732
- if (cmp(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
733
- }
734
-
735
- return { node: newNode, created: true };
736
- }
737
-
738
- /**
739
- * (Internal) Boolean wrapper around `_setKVNode`.
740
- *
741
- * Includes a map-mode update fast-path:
742
- * - If `isMapMode=true` and the key already exists in `_store`, then updating the value does not
743
- * require any tree search/rotation (tree shape depends only on key).
744
- * - This path is intentionally limited to `nextValue !== undefined` to preserve existing
745
- * semantics for `undefined` values.
746
- * @remarks Time O(log n) average, Space O(1)
747
- */
748
- protected _setKV(key: K, nextValue?: V): boolean {
749
- if (this._isMapMode) {
750
- const store = this._store;
751
- const node = store.get(key);
752
- if (node) {
753
- node.value = nextValue as V;
754
- return true;
755
- }
756
- }
757
-
758
- return this._setKVNode(key, nextValue) !== undefined;
759
- }
760
-
761
- /**
762
- * Insert/update using a hint node to speed up nearby insertions.
763
- *
764
- * close to the expected insertion position (often the previously returned node in a loop).
765
- *
766
- * When the hint is a good fit (sorted / nearly-sorted insertion), this can avoid most of the
767
- * normal root-to-leaf search and reduce constant factors.
768
- *
769
- * When the hint does not match (random workloads), this will fall back to the normal set path.
770
- * @remarks Time O(log n) average, Space O(1)
771
- */
772
- setWithHintNode(key: K, value: V, hint?: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
773
- if (!hint || !this.isRealNode(hint)) {
774
- return this._setKVNode(key, value)?.node;
775
- }
776
-
777
- const cmp = this._compare.bind(this);
778
- const c0 = cmp(key, hint.key);
779
- if (c0 === 0) {
780
- hint.value = value;
781
- if (this._isMapMode) this._store.set(key, hint);
782
- return hint;
783
- }
784
-
785
- if (c0 < 0) {
786
- // Ultra-fast path: direct attach if the target slot is empty.
787
- if (!this.isRealNode(hint.left)) {
788
- const newNode = this.createNode(key, value);
789
- if (!this.isRealNode(newNode)) return undefined;
790
- this._attachNewNode(hint, 'left', newNode);
791
- if (this._isMapMode) this._store.set(key, newNode);
792
- this._size++;
793
- // Maintain header/min/max caches.
794
- const NIL = this.NIL;
795
- const hMin = this._header._left ?? NIL;
796
- if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
797
- const hMax = this._header._right ?? NIL;
798
- if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
799
- return newNode;
800
- }
801
-
802
- const pred = this._predecessorOf(hint);
803
- if (pred && cmp(pred.key, key) >= 0) {
804
- return this._setKVNode(key, value)?.node;
805
- }
806
-
807
- // Try attach as right of pred.
808
- if (pred && !this.isRealNode(pred.right)) {
809
- const newNode = this.createNode(key, value);
810
- if (!this.isRealNode(newNode)) return undefined;
811
- this._attachNewNode(pred, 'right', newNode);
812
- if (this._isMapMode) this._store.set(key, newNode);
813
- this._size++;
814
- // Maintain header/min/max caches.
815
- const NIL = this.NIL;
816
- const hMin = this._header._left ?? NIL;
817
- if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
818
- const hMax = this._header._right ?? NIL;
819
- if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
820
- return newNode;
821
- }
822
-
823
- /* istanbul ignore next -- structurally unreachable: predecessor never has a right child (it's the max of left subtree) */
824
- return this._setKVNode(key, value)?.node;
825
- }
826
-
827
- // c0 > 0
828
- // Ultra-fast path: direct attach if the target slot is empty.
829
- if (!this.isRealNode(hint.right)) {
830
- const newNode = this.createNode(key, value);
831
- if (!this.isRealNode(newNode)) return undefined;
832
- this._attachNewNode(hint, 'right', newNode);
833
- if (this._isMapMode) this._store.set(key, newNode);
834
- this._size++;
835
- // Maintain header/min/max caches.
836
- const NIL = this.NIL;
837
- const hMin = this._header._left ?? NIL;
838
- if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
839
- const hMax = this._header._right ?? NIL;
840
- if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
841
- return newNode;
842
- }
843
-
844
- const succ = this._successorOf(hint);
845
- if (succ && cmp(succ.key, key) <= 0) {
846
- return this._setKVNode(key, value)?.node;
847
- }
848
-
849
- if (succ && !this.isRealNode(succ.left)) {
850
- const newNode = this.createNode(key, value);
851
- if (!this.isRealNode(newNode)) return undefined;
852
- this._attachNewNode(succ, 'left', newNode);
853
- if (this._isMapMode) this._store.set(key, newNode);
854
- this._size++;
855
- // Maintain header/min/max caches.
856
- const NIL = this.NIL;
857
- const hMin = this._header._left ?? NIL;
858
- if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
859
- const hMax = this._header._right ?? NIL;
860
- if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
861
- return newNode;
862
- }
863
-
864
- /* istanbul ignore next -- structurally unreachable: successor never has a left child (it's the min of right subtree) */
865
- return this._setKVNode(key, value)?.node;
866
- }
867
-
868
- /**
869
- * Boolean wrapper for setWithHintNode.
870
- * @remarks Time O(log n) average, Space O(1)
871
- */
872
- setWithHint(key: K, value: V, hint?: RedBlackTreeNode<K, V>): boolean {
873
- return this.setWithHintNode(key, value, hint) !== undefined;
874
- }
875
-
876
- /**
877
- * Insert or update a key/value (map mode) or key-only (set mode).
878
- *
879
- * This method is optimized for:
880
- * - monotonic inserts via min/max boundary fast paths
881
- * - updates via a single-pass search (no double walk)
882
- *
883
- * @remarks Time O(log n) average, Space O(1)
884
-
885
-
886
-
887
-
888
-
889
-
890
-
891
-
892
-
893
-
894
-
895
-
896
-
897
-
898
-
899
-
900
-
901
-
902
-
903
-
904
-
905
-
906
-
907
-
908
-
909
-
910
-
911
-
912
-
913
-
914
-
915
-
916
-
917
-
918
-
919
-
920
-
921
-
922
-
923
-
924
-
925
-
926
-
927
-
928
-
929
-
930
-
931
-
932
-
933
-
934
-
935
-
936
-
937
-
938
-
939
-
940
-
941
-
942
-
943
-
944
-
945
-
946
-
947
-
948
-
949
-
950
-
951
-
952
-
953
-
954
-
955
-
956
-
957
-
958
-
959
-
960
-
961
-
962
-
963
-
964
-
965
-
966
-
967
-
968
-
969
-
970
-
971
-
972
-
973
-
974
-
975
-
976
-
977
-
978
-
979
-
980
-
981
-
982
-
983
-
984
-
985
-
986
-
987
-
988
-
989
-
990
-
991
-
992
-
993
-
994
-
995
-
996
-
997
-
998
-
999
-
1000
-
1001
-
1002
-
1003
-
1004
-
1005
-
1006
-
1007
-
1008
-
1009
-
1010
-
1011
-
1012
-
1013
-
1014
-
1015
-
1016
-
1017
-
1018
-
1019
-
1020
-
1021
-
1022
-
1023
-
1024
-
1025
-
1026
-
1027
-
1028
-
1029
-
1030
-
1031
-
1032
- * @example
1033
- * // basic Red-Black Tree with simple number keys
1034
- * // Create a simple Red-Black Tree with numeric keys
1035
- * const tree = new RedBlackTree([5, 2, 8, 1, 9]);
1036
- *
1037
- * tree.print();
1038
- * // _2___
1039
- * // / \
1040
- * // 1 _8_
1041
- * // / \
1042
- * // 5 9
1043
- *
1044
- * // Verify the tree maintains sorted order
1045
- * console.log([...tree.keys()]); // [1, 2, 5, 8, 9];
1046
- *
1047
- * // Check size
1048
- * console.log(tree.size); // 5;
454
+ * @example
455
+ * // basic Red-Black Tree with simple number keys
456
+ * // Create a simple Red-Black Tree with numeric keys
457
+ * const tree = new RedBlackTree([5, 2, 8, 1, 9]);
458
+ *
459
+ * tree.print();
460
+ * // _2___
461
+ * // / \
462
+ * // 1 _8_
463
+ * // / \
464
+ * // 5 9
465
+ *
466
+ * // Verify the tree maintains sorted order
467
+ * console.log([...tree.keys()]); // [1, 2, 5, 8, 9];
468
+ *
469
+ * // Check size
470
+ * console.log(tree.size); // 5;
1049
471
  */
1050
472
  override set(
1051
473
  keyNodeOrEntry: K | RedBlackTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
@@ -1054,24 +476,19 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1054
476
  // Common path: tree.set(key, value) or tree.set([key, value]).
1055
477
  if (!this.isNode(keyNodeOrEntry)) {
1056
478
  if (keyNodeOrEntry === null || keyNodeOrEntry === undefined) return false;
1057
-
1058
479
  if (this.isEntry(keyNodeOrEntry)) {
1059
480
  const key = keyNodeOrEntry[0];
1060
481
  if (key === null || key === undefined) return false;
1061
482
  const nextValue = value ?? keyNodeOrEntry[1];
1062
483
  return this._setKV(key, nextValue);
1063
484
  }
1064
-
1065
485
  // key-only
1066
486
  return this._setKV(keyNodeOrEntry, value);
1067
487
  }
1068
-
1069
488
  // Node insertion path (advanced usage)
1070
489
  const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
1071
490
  if (!this.isRealNode(newNode)) return false;
1072
-
1073
491
  const insertStatus = this._insert(newNode);
1074
-
1075
492
  if (insertStatus === 'CREATED') {
1076
493
  if (this.isRealNode(this._root)) {
1077
494
  this._root.color = 'BLACK';
@@ -1107,186 +524,34 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1107
524
  * @remarks Time O(log n) average, Space O(1)
1108
525
  * @param keyNodeEntryRawOrPredicate - Key, node, or [key, value] entry identifying the node to delete.
1109
526
  * @returns Array with deletion metadata (removed node, rebalancing hint if any).
1110
-
1111
-
1112
-
1113
-
1114
-
1115
-
1116
-
1117
-
1118
-
1119
-
1120
-
1121
-
1122
-
1123
-
1124
-
1125
-
1126
-
1127
-
1128
-
1129
-
1130
-
1131
-
1132
-
1133
-
1134
-
1135
-
1136
-
1137
-
1138
-
1139
-
1140
-
1141
-
1142
-
1143
-
1144
-
1145
-
1146
-
1147
-
1148
-
1149
-
1150
-
1151
-
1152
-
1153
-
1154
-
1155
-
1156
-
1157
-
1158
-
1159
-
1160
-
1161
-
1162
-
1163
-
1164
-
1165
-
1166
-
1167
-
1168
-
1169
-
1170
-
1171
-
1172
-
1173
-
1174
-
1175
-
1176
-
1177
-
1178
-
1179
-
1180
-
1181
-
1182
-
1183
-
1184
-
1185
-
1186
-
1187
-
1188
-
1189
-
1190
-
1191
-
1192
-
1193
-
1194
-
1195
-
1196
-
1197
-
1198
-
1199
-
1200
-
1201
-
1202
-
1203
-
1204
-
1205
-
1206
-
1207
-
1208
-
1209
-
1210
-
1211
-
1212
-
1213
-
1214
-
1215
-
1216
-
1217
-
1218
-
1219
-
1220
-
1221
-
1222
-
1223
-
1224
-
1225
-
1226
-
1227
-
1228
-
1229
-
1230
-
1231
-
1232
-
1233
-
1234
-
1235
-
1236
-
1237
-
1238
-
1239
-
1240
-
1241
-
1242
-
1243
-
1244
-
1245
-
1246
-
1247
-
1248
-
1249
-
1250
-
1251
-
1252
-
1253
-
1254
-
1255
-
1256
-
1257
-
1258
-
1259
-
1260
- * @example
1261
- * // Remove and rebalance
1262
- * const rbt = new RedBlackTree<number>([10, 5, 15, 3, 7]);
1263
- * rbt.delete(5);
1264
- * console.log(rbt.has(5)); // false;
1265
- * console.log(rbt.size); // 4;
527
+ * @example
528
+ * // Remove and rebalance
529
+ * const rbt = new RedBlackTree<number>([10, 5, 15, 3, 7]);
530
+ * rbt.delete(5);
531
+ * console.log(rbt.has(5)); // false;
532
+ * console.log(rbt.size); // 4;
1266
533
  */
1267
534
  override delete(
1268
535
  keyNodeEntryRawOrPredicate: BTNRep<K, V, RedBlackTreeNode<K, V>> | NodePredicate<RedBlackTreeNode<K, V> | null>
1269
536
  ): boolean {
1270
537
  if (keyNodeEntryRawOrPredicate === null) return false;
1271
-
1272
538
  let nodeToDelete: OptNode<RedBlackTreeNode<K, V>>;
1273
539
  if (this._isPredicate(keyNodeEntryRawOrPredicate)) nodeToDelete = this.getNode(keyNodeEntryRawOrPredicate);
1274
- else nodeToDelete = this.isRealNode(keyNodeEntryRawOrPredicate) ? keyNodeEntryRawOrPredicate : this.getNode(keyNodeEntryRawOrPredicate);
1275
-
540
+ else
541
+ nodeToDelete = this.isRealNode(keyNodeEntryRawOrPredicate)
542
+ ? keyNodeEntryRawOrPredicate
543
+ : this.getNode(keyNodeEntryRawOrPredicate);
1276
544
  if (!nodeToDelete) {
1277
545
  return false;
1278
546
  }
1279
-
1280
547
  // Track min/max cache updates before structural modifications.
1281
548
  const willDeleteMin = nodeToDelete === this._minNode;
1282
549
  const willDeleteMax = nodeToDelete === this._maxNode;
1283
550
  const nextMin = willDeleteMin ? this._successorOf(nodeToDelete) : undefined;
1284
551
  const nextMax = willDeleteMax ? this._predecessorOf(nodeToDelete) : undefined;
1285
-
1286
552
  let originalColor = nodeToDelete.color;
1287
553
  const NIL = this.NIL;
1288
554
  let replacementNode: RedBlackTreeNode<K, V> = NIL;
1289
-
1290
555
  if (!this.isRealNode(nodeToDelete.left)) {
1291
556
  // No real left child → replace with right (may be NIL)
1292
557
  replacementNode = nodeToDelete.right ?? NIL;
@@ -1301,7 +566,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1301
566
  if (successor) {
1302
567
  originalColor = successor.color;
1303
568
  replacementNode = successor.right ?? NIL;
1304
-
1305
569
  if (successor.parent === nodeToDelete) {
1306
570
  // Even if replacementNode is NIL, set its parent for fixup
1307
571
  replacementNode.parent = successor;
@@ -1312,7 +576,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1312
576
  successor.right.parent = successor;
1313
577
  }
1314
578
  }
1315
-
1316
579
  this._transplant(nodeToDelete, successor);
1317
580
  successor.left = nodeToDelete.left;
1318
581
  if (successor.left) {
@@ -1323,10 +586,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1323
586
  }
1324
587
  if (this._isMapMode) this._store.delete(nodeToDelete.key);
1325
588
  this._size--;
1326
-
1327
589
  // Update order-statistic counts from replacement up to root
1328
- this._updateCountAlongPath(replacementNode?.parent as RedBlackTreeNode<K, V> | undefined ?? replacementNode);
1329
-
590
+ this._updateCountAlongPath((replacementNode?.parent as RedBlackTreeNode<K, V> | undefined) ?? replacementNode);
1330
591
  // Update min/max caches.
1331
592
  if (this._size <= 0) {
1332
593
  this._setMinCache(undefined);
@@ -1342,325 +603,319 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1342
603
  this._setMaxCache(this.isRealNode(this._root) ? this.getRightMost(n => n, this._root) : undefined);
1343
604
  }
1344
605
  }
1345
-
1346
- if (originalColor === 'BLACK') {
1347
- this._deleteFixup(replacementNode);
606
+ if (originalColor === 'BLACK') {
607
+ this._deleteFixup(replacementNode);
608
+ }
609
+ return true;
610
+ }
611
+
612
+ /**
613
+ * Transform entries into a like-kind red-black tree with possibly different key/value types.
614
+ * @remarks Time O(n) average, Space O(n)
615
+ * @template MK
616
+ * @template MV
617
+ * @template MR
618
+ * @param callback - Mapping function from (key, value, index, tree) to a new [key, value].
619
+ * @param [options] - See parameter type for details.
620
+ * @param [thisArg] - See parameter type for details.
621
+ * @returns A new RedBlackTree with mapped entries.
622
+ */
623
+ /**
624
+ * Red-Black trees are self-balancing — `perfectlyBalance` rebuilds via
625
+ * sorted bulk insert, which naturally produces a balanced RBT.
626
+ * @remarks Time O(N), Space O(N)
627
+ * @example
628
+ * // Rebalance tree
629
+ * const rbt = new RedBlackTree<number>([1, 2, 3, 4, 5]);
630
+ * rbt.perfectlyBalance();
631
+ * console.log(rbt.isAVLBalanced()); // true;
632
+ */
633
+ override perfectlyBalance(_iterationType?: IterationType): boolean {
634
+ // Extract sorted entries, clear, re-insert — RBT self-balances on insert
635
+ const entries: [K, V | undefined][] = [];
636
+ for (const [key, value] of this) entries.push([key, value]);
637
+ if (entries.length <= 1) return true;
638
+ this.clear();
639
+ this.setMany(
640
+ entries.map(([k]) => k),
641
+ entries.map(([, v]) => v),
642
+ true // isBalanceAdd
643
+ );
644
+ return true;
645
+ }
646
+
647
+ /**
648
+ * Transform to new tree
649
+ * @example
650
+ * // Transform to new tree
651
+ * const rbt = new RedBlackTree<number, number>([
652
+ * [1, 10],
653
+ * [2, 20]
654
+ * ]);
655
+ * const doubled = rbt.map((v, k) => [k, (v ?? 0) * 2] as [number, number]);
656
+ * console.log([...doubled.values()]); // [20, 40];
657
+ */
658
+ override map<MK = K, MV = V, MR = any>(
659
+ callback: EntryCallback<K, V | undefined, [MK, MV]>,
660
+ options?: Partial<RedBlackTreeOptions<MK, MV, MR>>,
661
+ thisArg?: unknown
662
+ ): RedBlackTree<MK, MV, MR> {
663
+ const out = this._createLike<MK, MV, MR>([], options);
664
+ let index = 0;
665
+ for (const [key, value] of this) {
666
+ out.set(callback.call(thisArg, value, key, index++, this));
667
+ }
668
+ return out;
669
+ }
670
+
671
+ /**
672
+ * (Internal) Find a node by key using a tight BST walk (no allocations).
673
+ *
674
+ * NOTE: This uses `header.parent` as the canonical root pointer.
675
+ * @remarks Time O(log n) average, Space O(1)
676
+ */
677
+ protected _findNodeByKey(key: K): RedBlackTreeNode<K, V> | undefined {
678
+ const NIL = this.NIL;
679
+ const cmp = this._compare.bind(this);
680
+ let cur = this._header.parent ?? NIL;
681
+ while (cur !== NIL) {
682
+ const c = cmp(key, cur.key);
683
+ if (c < 0) cur = cur.left ?? NIL;
684
+ else if (c > 0) cur = cur.right ?? NIL;
685
+ else return cur;
686
+ }
687
+ return undefined;
688
+ }
689
+
690
+ /**
691
+ * (Internal) In-order predecessor of a node in a BST.
692
+ * @remarks Time O(log n) average, Space O(1)
693
+ */
694
+ protected _predecessorOf(node: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
695
+ const NIL = this.NIL;
696
+ if (node.left && node.left !== NIL) {
697
+ let cur = node.left;
698
+ while (cur.right && cur.right !== NIL) cur = cur.right;
699
+ return cur;
700
+ }
701
+ let cur: RedBlackTreeNode<K, V> | undefined = node;
702
+ let p = node.parent;
703
+ while (p && cur === p.left) {
704
+ cur = p;
705
+ p = p.parent;
706
+ }
707
+ return p;
708
+ }
709
+
710
+ /**
711
+ * (Internal) In-order successor of a node in a BST.
712
+ * @remarks Time O(log n) average, Space O(1)
713
+ */
714
+ protected _successorOf(node: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
715
+ const NIL = this.NIL;
716
+ if (node.right && node.right !== NIL) {
717
+ let cur = node.right;
718
+ while (cur.left && cur.left !== NIL) cur = cur.left;
719
+ return cur;
720
+ }
721
+ let cur: RedBlackTreeNode<K, V> | undefined = node;
722
+ let p = node.parent;
723
+ while (p && cur === p.right) {
724
+ cur = p;
725
+ p = p.parent;
1348
726
  }
727
+ return p;
728
+ }
1349
729
 
1350
- return true;
730
+ /**
731
+ * (Internal) Attach a new node directly under a known parent/side (no search).
732
+ *
733
+ * This is a performance-oriented helper used by boundary fast paths and hinted insertion.
734
+ * It will:
735
+ * - wire parent/child pointers (using accessors, so parent pointers are updated)
736
+ * - initialize children to NIL
737
+ * - mark the new node RED, then run insert fix-up
738
+ *
739
+ * Precondition: the chosen slot (parent.left/parent.right) is empty (NIL/null/undefined).
740
+ * @remarks Time O(log n) average, Space O(1)
741
+ */
742
+ protected _attachNewNode(parent: RedBlackTreeNode<K, V>, side: 'left' | 'right', node: RedBlackTreeNode<K, V>): void {
743
+ const NIL = this.NIL;
744
+ node.parent = parent;
745
+ if (side === 'left') parent.left = node;
746
+ else parent.right = node;
747
+ node.left = NIL;
748
+ node.right = NIL;
749
+ node.color = 'RED';
750
+ this._updateCountAlongPath(node);
751
+ this._insertFixup(node);
752
+ if (this.isRealNode(this._root)) this._root.color = 'BLACK';
1351
753
  }
1352
754
 
1353
755
  /**
1354
- * Transform entries into a like-kind red-black tree with possibly different key/value types.
1355
- * @remarks Time O(n) average, Space O(n)
1356
- * @template MK
1357
- * @template MV
1358
- * @template MR
1359
- * @param callback - Mapping function from (key, value, index, tree) to a new [key, value].
1360
- * @param [options] - See parameter type for details.
1361
- * @param [thisArg] - See parameter type for details.
1362
- * @returns A new RedBlackTree with mapped entries.
756
+ * (Internal) a single source of truth for min/max is header._left/_right.
757
+ * Keep legacy _minNode/_maxNode mirrored for compatibility.
758
+ * @remarks Time O(1), Space O(1)
1363
759
  */
1364
760
  /**
1365
- * Red-Black trees are self-balancing `perfectlyBalance` rebuilds via
1366
- * sorted bulk insert, which naturally produces a balanced RBT.
1367
- * @remarks Time O(N), Space O(N)
1368
-
1369
-
1370
-
1371
-
1372
-
1373
-
1374
-
1375
-
1376
-
1377
-
1378
-
1379
-
1380
-
1381
-
1382
-
1383
-
1384
-
1385
-
1386
-
1387
-
1388
-
1389
-
1390
-
1391
-
1392
-
1393
-
1394
-
1395
-
1396
-
1397
-
1398
-
1399
-
1400
-
1401
-
1402
-
1403
-
1404
-
1405
-
1406
-
1407
-
1408
-
1409
-
1410
-
1411
-
1412
-
1413
-
1414
-
1415
-
1416
-
1417
-
1418
-
1419
-
1420
-
1421
-
1422
-
1423
-
1424
-
1425
-
1426
-
1427
-
1428
-
1429
-
1430
-
1431
-
1432
-
1433
-
1434
-
1435
-
1436
-
1437
-
1438
-
1439
-
1440
-
1441
-
1442
-
1443
-
1444
-
1445
-
1446
-
1447
-
1448
-
1449
-
1450
-
1451
-
1452
-
1453
-
1454
-
1455
-
1456
-
1457
-
1458
-
1459
-
1460
-
1461
-
1462
-
1463
-
1464
-
1465
-
1466
-
1467
-
1468
-
1469
-
1470
-
1471
-
1472
-
1473
-
1474
-
1475
-
1476
-
1477
- * @example
1478
- * // Rebalance tree
1479
- * const rbt = new RedBlackTree<number>([1, 2, 3, 4, 5]);
1480
- * rbt.perfectlyBalance();
1481
- * console.log(rbt.isAVLBalanced()); // true;
761
+ * (Internal) Update min cache pointers (header._left is the canonical min pointer).
762
+ * @remarks Time O(1), Space O(1)
1482
763
  */
1483
- override perfectlyBalance(_iterationType?: IterationType): boolean {
1484
- // Extract sorted entries, clear, re-insert — RBT self-balances on insert
1485
- const entries: [K, V | undefined][] = [];
1486
- for (const [key, value] of this) entries.push([key, value]);
1487
- if (entries.length <= 1) return true;
1488
- this.clear();
1489
- this.setMany(
1490
- entries.map(([k]) => k),
1491
- entries.map(([, v]) => v),
1492
- true // isBalanceAdd
1493
- );
1494
- return true;
764
+ protected _setMinCache(node: RedBlackTreeNode<K, V> | undefined): void {
765
+ this._minNode = node;
766
+ this._header._left = node ?? this.NIL;
1495
767
  }
1496
768
 
1497
- /**
1498
- * Transform to new tree
1499
-
1500
-
1501
-
1502
-
1503
-
1504
-
1505
-
1506
-
1507
-
1508
-
1509
-
1510
-
1511
-
1512
-
1513
-
1514
-
1515
-
1516
-
1517
-
1518
-
1519
-
1520
-
1521
-
1522
-
1523
-
1524
-
1525
-
1526
-
1527
-
1528
-
1529
-
1530
-
1531
-
1532
-
1533
-
1534
-
1535
-
1536
-
1537
-
1538
-
1539
-
1540
-
1541
-
1542
-
1543
-
1544
-
1545
-
1546
-
1547
-
1548
-
1549
-
1550
-
1551
-
1552
-
1553
-
1554
-
1555
-
1556
-
1557
-
1558
-
1559
-
1560
-
1561
-
1562
-
1563
-
1564
-
1565
-
1566
-
1567
-
1568
-
1569
-
1570
-
1571
-
1572
-
1573
-
1574
-
1575
-
1576
-
1577
-
1578
-
1579
-
1580
-
1581
-
1582
-
1583
-
1584
-
1585
-
1586
-
1587
-
1588
-
1589
-
1590
-
1591
-
1592
-
1593
-
1594
-
1595
-
1596
-
1597
-
1598
-
1599
-
1600
-
1601
-
1602
-
1603
-
1604
-
1605
-
1606
-
1607
-
1608
-
1609
-
1610
-
1611
-
1612
-
1613
-
1614
-
1615
-
1616
-
1617
-
1618
-
1619
-
1620
-
1621
-
1622
-
1623
-
1624
-
1625
-
1626
-
1627
-
1628
-
1629
-
1630
-
1631
-
1632
-
1633
-
1634
-
1635
-
1636
-
1637
-
1638
-
1639
-
1640
-
1641
-
1642
-
1643
-
1644
-
1645
-
1646
- * @example
1647
- * // Transform to new tree
1648
- * const rbt = new RedBlackTree<number, number>([[1, 10], [2, 20]]);
1649
- * const doubled = rbt.map((v, k) => [k, (v ?? 0) * 2] as [number, number]);
1650
- * console.log([...doubled.values()]); // [20, 40];
769
+ /**
770
+ * (Internal) Update max cache pointers (header._right is the canonical max pointer).
771
+ * @remarks Time O(1), Space O(1)
1651
772
  */
1652
- override map<MK = K, MV = V, MR = any>(
1653
- callback: EntryCallback<K, V | undefined, [MK, MV]>,
1654
- options?: Partial<RedBlackTreeOptions<MK, MV, MR>>,
1655
- thisArg?: unknown
1656
- ): RedBlackTree<MK, MV, MR> {
1657
- const out = this._createLike<MK, MV, MR>([], options);
773
+ protected _setMaxCache(node: RedBlackTreeNode<K, V> | undefined): void {
774
+ this._maxNode = node;
775
+ this._header._right = node ?? this.NIL;
776
+ }
1658
777
 
1659
- let index = 0;
1660
- for (const [key, value] of this) {
1661
- out.set(callback.call(thisArg, value, key, index++, this));
778
+ /**
779
+ * (Internal) Core set implementation returning the affected node.
780
+ *
781
+ * Hot path goals:
782
+ * - Avoid double walks (search+insert): do a single traversal that either updates or inserts.
783
+ * - Use header min/max caches to fast-path boundary inserts.
784
+ * - Keep header._left/_right as canonical min/max pointers.
785
+ *
786
+ * Return value:
787
+ * - `{ node, created:false }` when an existing key is updated
788
+ * - `{ node, created:true }` when a new node is inserted
789
+ * - `undefined` only on unexpected internal failure.
790
+ * @remarks Time O(log n) average, Space O(1)
791
+ */
792
+ protected _setKVNode(key: K, nextValue?: V): { node: RedBlackTreeNode<K, V>; created: boolean } | undefined {
793
+ const NIL = this.NIL;
794
+ const comparator = this._comparator;
795
+ // Read via header to avoid undefined checks (header uses NIL when empty).
796
+ const header = this._header;
797
+ const minN = header._left ?? NIL;
798
+ if (minN !== NIL) {
799
+ const cMin = comparator(key, minN.key);
800
+ if (cMin === 0) {
801
+ minN.value = nextValue as V;
802
+ if (this._isMapMode) this._store.set(key, minN);
803
+ return { node: minN, created: false };
804
+ }
805
+ // Boundary attach: if key is smaller than current min and min has no left child.
806
+ // Inline NIL/null/undefined check to avoid isRealNode overhead on hot path.
807
+ const minL = minN.left;
808
+ if (cMin < 0 && (minL === NIL || minL === null || minL === undefined)) {
809
+ const newNode = this.createNode(key, nextValue);
810
+ this._attachNewNode(minN, 'left', newNode);
811
+ if (this._isMapMode) this._store.set(newNode.key, newNode);
812
+ this._size++;
813
+ this._setMinCache(newNode);
814
+ // If max is not initialized yet (tree had 0/1 nodes), mirror max too.
815
+ if (header._right === NIL) this._setMaxCache(newNode);
816
+ return { node: newNode, created: true };
817
+ }
818
+ // Only touch max when key is not less than min.
819
+ if (cMin > 0) {
820
+ const maxN = header._right ?? NIL;
821
+ // Boundary attach: if key is greater than current max and max has no right child.
822
+ const cMax = comparator(key, maxN.key);
823
+ if (cMax === 0) {
824
+ maxN.value = nextValue as V;
825
+ if (this._isMapMode) this._store.set(key, maxN);
826
+ return { node: maxN, created: false };
827
+ }
828
+ const maxR = maxN.right;
829
+ if (cMax > 0 && (maxR === NIL || maxR === null || maxR === undefined)) {
830
+ const newNode = this.createNode(key, nextValue);
831
+ this._attachNewNode(maxN, 'right', newNode);
832
+ if (this._isMapMode) this._store.set(newNode.key, newNode);
833
+ this._size++;
834
+ this._setMaxCache(newNode);
835
+ if (header._left === NIL) this._setMinCache(newNode);
836
+ return { node: newNode, created: true };
837
+ }
838
+ }
1662
839
  }
1663
- return out;
840
+ // Normal path: single-pass search + insert/update (avoid double-walking the tree).
841
+ const cmp = comparator;
842
+ const isMapMode = this._isMapMode;
843
+ const store = this._store;
844
+ let current = this._header.parent ?? NIL;
845
+ let parent: RedBlackTreeNode<K, V> | undefined;
846
+ let lastCompared = 0;
847
+ while (current !== NIL) {
848
+ parent = current;
849
+ lastCompared = cmp(key, current.key);
850
+ if (lastCompared < 0) current = current.left ?? NIL;
851
+ else if (lastCompared > 0) current = current.right ?? NIL;
852
+ else {
853
+ // Update existing.
854
+ current.value = nextValue as V;
855
+ if (isMapMode) store.set(key, current);
856
+ return { node: current, created: false };
857
+ }
858
+ }
859
+ // Insert new.
860
+ const newNode = this.createNode(key, nextValue);
861
+ // createNode always returns a real node in RedBlackTree.
862
+ newNode.parent = parent;
863
+ if (!parent) {
864
+ this._setRoot(newNode);
865
+ } else if (lastCompared < 0) {
866
+ parent.left = newNode;
867
+ } else {
868
+ parent.right = newNode;
869
+ }
870
+ newNode.left = NIL;
871
+ newNode.right = NIL;
872
+ newNode.color = 'RED';
873
+ this._updateCountAlongPath(newNode);
874
+ this._insertFixup(newNode);
875
+ if (this.isRealNode(this._root)) this._root.color = 'BLACK';
876
+ else return undefined;
877
+ if (isMapMode) store.set(newNode.key, newNode);
878
+ this._size++;
879
+ // Maintain min/max caches on insertion (header.left/right are canonical).
880
+ const hMin = this._header._left ?? NIL;
881
+ const hMax = this._header._right ?? NIL;
882
+ // Fast-path: empty tree or attaching directly to an extreme.
883
+ if (hMin === NIL || hMax === NIL) {
884
+ this._setMinCache(newNode);
885
+ this._setMaxCache(newNode);
886
+ } else if (parent === hMax && lastCompared > 0) {
887
+ /* istanbul ignore next -- boundary fast-paths at top of _setKVNode intercept boundary inserts before normal path */ /* istanbul ignore next */
888
+ this._setMaxCache(newNode);
889
+ } else if (parent === hMin && lastCompared < 0) {
890
+ /* istanbul ignore next */ /* istanbul ignore next */
891
+ this._setMinCache(newNode);
892
+ } else {
893
+ if (cmp(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
894
+ if (cmp(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
895
+ }
896
+ return { node: newNode, created: true };
897
+ }
898
+
899
+ /**
900
+ * (Internal) Boolean wrapper around `_setKVNode`.
901
+ *
902
+ * Includes a map-mode update fast-path:
903
+ * - If `isMapMode=true` and the key already exists in `_store`, then updating the value does not
904
+ * require any tree search/rotation (tree shape depends only on key).
905
+ * - This path is intentionally limited to `nextValue !== undefined` to preserve existing
906
+ * semantics for `undefined` values.
907
+ * @remarks Time O(log n) average, Space O(1)
908
+ */
909
+ protected _setKV(key: K, nextValue?: V): boolean {
910
+ if (this._isMapMode) {
911
+ const store = this._store;
912
+ const node = store.get(key);
913
+ if (node) {
914
+ node.value = nextValue as V;
915
+ return true;
916
+ }
917
+ }
918
+ return this._setKVNode(key, nextValue) !== undefined;
1664
919
  }
1665
920
 
1666
921
  /**
@@ -1716,7 +971,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1716
971
  newNode: RedBlackTreeNode<K, V>
1717
972
  ): RedBlackTreeNode<K, V> {
1718
973
  newNode.color = oldNode.color;
1719
-
1720
974
  return super._replaceNode(oldNode, newNode);
1721
975
  }
1722
976
 
@@ -1729,11 +983,9 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1729
983
  protected _insert(node: RedBlackTreeNode<K, V>): CRUD {
1730
984
  const NIL = this.NIL;
1731
985
  const cmp = this._compare.bind(this);
1732
-
1733
986
  let current = this._header.parent ?? NIL;
1734
987
  let parent: RedBlackTreeNode<K, V> | undefined;
1735
988
  let lastCompared = 0;
1736
-
1737
989
  while (current !== NIL) {
1738
990
  parent = current;
1739
991
  lastCompared = cmp(node.key, current.key);
@@ -1746,9 +998,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1746
998
  return 'UPDATED';
1747
999
  }
1748
1000
  }
1749
-
1750
1001
  node.parent = parent;
1751
-
1752
1002
  if (!parent) {
1753
1003
  this._setRoot(node);
1754
1004
  } else if (lastCompared < 0) {
@@ -1756,14 +1006,11 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1756
1006
  } else {
1757
1007
  parent.right = node;
1758
1008
  }
1759
-
1760
1009
  node.left = NIL;
1761
1010
  node.right = NIL;
1762
1011
  node.color = 'RED';
1763
-
1764
1012
  // Update counts along insertion path before fixup
1765
1013
  this._updateCountAlongPath(node);
1766
-
1767
1014
  this._insertFixup(node);
1768
1015
  return 'CREATED';
1769
1016
  }
@@ -1783,7 +1030,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1783
1030
  } else {
1784
1031
  u.parent.right = v;
1785
1032
  }
1786
-
1787
1033
  if (v) {
1788
1034
  v.parent = u.parent;
1789
1035
  }
@@ -1798,14 +1044,11 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1798
1044
  protected _insertFixup(z: RedBlackTreeNode<K, V> | undefined): void {
1799
1045
  const leftRotate = this._leftRotate.bind(this);
1800
1046
  const rightRotate = this._rightRotate.bind(this);
1801
-
1802
1047
  while (z) {
1803
1048
  const p = z.parent;
1804
1049
  if (!p || p.color !== 'RED') break;
1805
-
1806
1050
  const gp = p.parent;
1807
1051
  if (!gp) break;
1808
-
1809
1052
  if (p === gp.left) {
1810
1053
  const y = gp.right;
1811
1054
  if (y?.color === 'RED') {
@@ -1815,12 +1058,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1815
1058
  z = gp;
1816
1059
  continue;
1817
1060
  }
1818
-
1819
1061
  if (z === p.right) {
1820
1062
  z = p;
1821
1063
  leftRotate(z);
1822
1064
  }
1823
-
1824
1065
  const p2 = z?.parent;
1825
1066
  const gp2 = p2?.parent;
1826
1067
  if (p2 && gp2) {
@@ -1837,12 +1078,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1837
1078
  z = gp;
1838
1079
  continue;
1839
1080
  }
1840
-
1841
1081
  if (z === p.left) {
1842
1082
  z = p;
1843
1083
  rightRotate(z);
1844
1084
  }
1845
-
1846
1085
  const p2 = z?.parent;
1847
1086
  const gp2 = p2?.parent;
1848
1087
  if (p2 && gp2) {
@@ -1851,10 +1090,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1851
1090
  leftRotate(gp2);
1852
1091
  }
1853
1092
  }
1854
-
1855
1093
  break;
1856
1094
  }
1857
-
1858
1095
  if (this.isRealNode(this._root)) this._root.color = 'BLACK';
1859
1096
  }
1860
1097
 
@@ -1869,17 +1106,13 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1869
1106
  // `node` is the child that replaced the deleted node (may be NIL sentinel).
1870
1107
  // If RED → just recolor BLACK (trivial fix). If BLACK → double-black repair.
1871
1108
  if (!node) return;
1872
-
1873
1109
  const NIL = this.NIL;
1874
1110
  let current: RedBlackTreeNode<K, V> = node;
1875
-
1876
1111
  while (current !== this.root && current.color === 'BLACK') {
1877
1112
  const parent: RedBlackTreeNode<K, V> | undefined = current.parent;
1878
1113
  if (!parent) break;
1879
-
1880
1114
  const nodeIsLeft = current === parent.left;
1881
1115
  let sibling = nodeIsLeft ? parent.right : parent.left;
1882
-
1883
1116
  // Case 1: sibling is RED → rotate to get a BLACK sibling
1884
1117
  if (sibling && sibling.color === 'RED') {
1885
1118
  sibling.color = 'BLACK';
@@ -1892,12 +1125,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1892
1125
  sibling = parent.left;
1893
1126
  }
1894
1127
  }
1895
-
1896
1128
  const sibLeft = sibling?.left;
1897
1129
  const sibRight = sibling?.right;
1898
1130
  const sibLeftBlack = !sibLeft || sibLeft === NIL || sibLeft.color === 'BLACK';
1899
1131
  const sibRightBlack = !sibRight || sibRight === NIL || sibRight.color === 'BLACK';
1900
-
1901
1132
  if (sibLeftBlack && sibRightBlack) {
1902
1133
  // Case 2: sibling's children are both BLACK → recolor sibling RED, move up
1903
1134
  if (sibling) sibling.color = 'RED';
@@ -1933,7 +1164,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1933
1164
  current = this.root!;
1934
1165
  }
1935
1166
  }
1936
-
1937
1167
  current.color = 'BLACK';
1938
1168
  }
1939
1169
 
@@ -1947,16 +1177,12 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1947
1177
  if (!x || !x.right) {
1948
1178
  return;
1949
1179
  }
1950
-
1951
1180
  const y = x.right;
1952
1181
  x.right = y.left;
1953
-
1954
1182
  if (y.left && y.left !== this.NIL) {
1955
1183
  y.left.parent = x;
1956
1184
  }
1957
-
1958
1185
  y.parent = x.parent;
1959
-
1960
1186
  if (!x.parent) {
1961
1187
  this._setRoot(y);
1962
1188
  } else if (x === x.parent.left) {
@@ -1964,10 +1190,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1964
1190
  } else {
1965
1191
  x.parent.right = y;
1966
1192
  }
1967
-
1968
1193
  y.left = x;
1969
1194
  x.parent = y;
1970
-
1971
1195
  // Update counts: x first (now child), then y (now parent)
1972
1196
  this._updateCount(x);
1973
1197
  this._updateCount(y);
@@ -1983,16 +1207,12 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
1983
1207
  if (!y || !y.left) {
1984
1208
  return;
1985
1209
  }
1986
-
1987
1210
  const x = y.left;
1988
1211
  y.left = x.right;
1989
-
1990
1212
  if (x.right && x.right !== this.NIL) {
1991
1213
  x.right.parent = y;
1992
1214
  }
1993
-
1994
1215
  x.parent = y.parent;
1995
-
1996
1216
  if (!y.parent) {
1997
1217
  this._setRoot(x);
1998
1218
  } else if (y === y.parent.left) {
@@ -2000,10 +1220,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
2000
1220
  } else {
2001
1221
  y.parent.right = x;
2002
1222
  }
2003
-
2004
1223
  x.right = y;
2005
1224
  y.parent = x;
2006
-
2007
1225
  // Update counts: y first (now child), then x (now parent)
2008
1226
  this._updateCount(y);
2009
1227
  this._updateCount(x);