data-structure-typed 2.6.0 → 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.
- package/.github/workflows/ci.yml +7 -2
- package/.github/workflows/release-package.yml +9 -2
- package/docs-site-docusaurus/docs/api/classes/AVLTree.md +108 -108
- package/docs-site-docusaurus/docs/api/classes/BST.md +101 -101
- package/docs-site-docusaurus/docs/api/classes/BinaryIndexedTree.md +13 -13
- package/docs-site-docusaurus/docs/api/classes/BinaryTree.md +66 -66
- package/docs-site-docusaurus/docs/api/classes/Deque.md +235 -51
- package/docs-site-docusaurus/docs/api/classes/DirectedGraph.md +21 -21
- package/docs-site-docusaurus/docs/api/classes/DoublyLinkedList.md +231 -67
- package/docs-site-docusaurus/docs/api/classes/FibonacciHeap.md +9 -9
- package/docs-site-docusaurus/docs/api/classes/FibonacciHeapNode.md +1 -1
- package/docs-site-docusaurus/docs/api/classes/HashMap.md +14 -14
- package/docs-site-docusaurus/docs/api/classes/Heap.md +117 -34
- package/docs-site-docusaurus/docs/api/classes/IterableElementBase.md +83 -13
- package/docs-site-docusaurus/docs/api/classes/LinearBase.md +124 -20
- package/docs-site-docusaurus/docs/api/classes/LinearLinkedBase.md +140 -32
- package/docs-site-docusaurus/docs/api/classes/LinkedHashMap.md +23 -23
- package/docs-site-docusaurus/docs/api/classes/LinkedListQueue.md +159 -51
- package/docs-site-docusaurus/docs/api/classes/MapGraph.md +20 -20
- package/docs-site-docusaurus/docs/api/classes/Matrix.md +23 -23
- package/docs-site-docusaurus/docs/api/classes/MaxHeap.md +117 -34
- package/docs-site-docusaurus/docs/api/classes/MaxPriorityQueue.md +117 -34
- package/docs-site-docusaurus/docs/api/classes/MinHeap.md +117 -34
- package/docs-site-docusaurus/docs/api/classes/MinPriorityQueue.md +117 -34
- package/docs-site-docusaurus/docs/api/classes/PriorityQueue.md +117 -34
- package/docs-site-docusaurus/docs/api/classes/Queue.md +142 -34
- package/docs-site-docusaurus/docs/api/classes/RedBlackTree.md +117 -117
- package/docs-site-docusaurus/docs/api/classes/SegmentTree.md +8 -8
- package/docs-site-docusaurus/docs/api/classes/SinglyLinkedList.md +158 -50
- package/docs-site-docusaurus/docs/api/classes/SkipList.md +21 -21
- package/docs-site-docusaurus/docs/api/classes/Stack.md +108 -26
- package/docs-site-docusaurus/docs/api/classes/TreeMap.md +33 -33
- package/docs-site-docusaurus/docs/api/classes/TreeMultiMap.md +75 -39
- package/docs-site-docusaurus/docs/api/classes/TreeSet.md +301 -39
- package/docs-site-docusaurus/docs/api/classes/Trie.md +110 -28
- package/docs-site-docusaurus/docs/api/classes/UndirectedGraph.md +20 -20
- package/package.json +45 -46
- package/src/common/error.ts +15 -32
- package/src/common/index.ts +0 -3
- package/src/data-structures/base/iterable-element-base.ts +0 -3
- package/src/data-structures/base/linear-base.ts +2 -36
- package/src/data-structures/binary-tree/avl-tree.ts +31 -529
- package/src/data-structures/binary-tree/binary-indexed-tree.ts +47 -572
- package/src/data-structures/binary-tree/binary-tree.ts +326 -1311
- package/src/data-structures/binary-tree/bst.ts +158 -1082
- package/src/data-structures/binary-tree/red-black-tree.ts +451 -1290
- package/src/data-structures/binary-tree/segment-tree.ts +73 -351
- package/src/data-structures/binary-tree/tree-map.ts +462 -5124
- package/src/data-structures/binary-tree/tree-multi-map.ts +302 -4914
- package/src/data-structures/binary-tree/tree-multi-set.ts +284 -3972
- package/src/data-structures/binary-tree/tree-set.ts +338 -4836
- package/src/data-structures/graph/abstract-graph.ts +98 -167
- package/src/data-structures/graph/directed-graph.ts +137 -562
- package/src/data-structures/graph/map-graph.ts +0 -3
- package/src/data-structures/graph/undirected-graph.ts +132 -511
- package/src/data-structures/hash/hash-map.ts +154 -582
- package/src/data-structures/heap/heap.ts +200 -795
- package/src/data-structures/linked-list/doubly-linked-list.ts +121 -865
- package/src/data-structures/linked-list/singly-linked-list.ts +122 -794
- package/src/data-structures/linked-list/skip-linked-list.ts +211 -918
- package/src/data-structures/matrix/matrix.ts +179 -518
- package/src/data-structures/matrix/navigator.ts +0 -1
- package/src/data-structures/priority-queue/max-priority-queue.ts +1 -6
- package/src/data-structures/priority-queue/min-priority-queue.ts +6 -11
- package/src/data-structures/priority-queue/priority-queue.ts +1 -2
- package/src/data-structures/queue/deque.ts +214 -882
- package/src/data-structures/queue/queue.ts +102 -625
- package/src/data-structures/stack/stack.ts +76 -505
- package/src/data-structures/trie/trie.ts +98 -628
- package/src/types/common.ts +0 -10
- package/src/types/data-structures/binary-tree/bst.ts +0 -7
- package/src/types/data-structures/binary-tree/red-black-tree.ts +0 -1
- package/src/types/data-structures/graph/abstract-graph.ts +0 -2
- package/src/types/data-structures/hash/hash-map.ts +0 -3
- package/src/types/data-structures/hash/index.ts +0 -1
- package/src/types/data-structures/matrix/navigator.ts +0 -2
- package/src/types/utils/utils.ts +0 -7
- package/src/types/utils/validate-type.ts +0 -7
- package/src/utils/number.ts +0 -2
- 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,
|
|
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,170 +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
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
* @example
|
|
497
|
-
* // Remove all entries
|
|
498
|
-
* const rbt = new RedBlackTree<number>([1, 2, 3]);
|
|
499
|
-
* rbt.clear();
|
|
500
|
-
* 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;
|
|
501
331
|
*/
|
|
502
332
|
override clear() {
|
|
503
333
|
super.clear();
|
|
@@ -507,569 +337,137 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
507
337
|
this._setMaxCache(undefined);
|
|
508
338
|
}
|
|
509
339
|
|
|
510
|
-
|
|
511
340
|
/**
|
|
512
|
-
*
|
|
341
|
+
* Insert/update using a hint node to speed up nearby insertions.
|
|
513
342
|
*
|
|
514
|
-
*
|
|
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.
|
|
515
349
|
* @remarks Time O(log n) average, Space O(1)
|
|
516
350
|
*/
|
|
517
|
-
|
|
518
|
-
|
|
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
|
+
}
|
|
519
355
|
const cmp = this._compare.bind(this);
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
else if (c > 0) cur = cur.right ?? NIL;
|
|
526
|
-
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;
|
|
527
361
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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;
|
|
541
399
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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;
|
|
547
415
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
* (Internal) In-order successor of a node in a BST.
|
|
553
|
-
* @remarks Time O(log n) average, Space O(1)
|
|
554
|
-
*/
|
|
555
|
-
protected _successorOf(node: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
|
|
556
|
-
const NIL = this.NIL;
|
|
557
|
-
if (node.right && node.right !== NIL) {
|
|
558
|
-
let cur = node.right;
|
|
559
|
-
while (cur.left && cur.left !== NIL) cur = cur.left;
|
|
560
|
-
return cur;
|
|
416
|
+
const succ = this._successorOf(hint);
|
|
417
|
+
if (succ && cmp(succ.key, key) <= 0) {
|
|
418
|
+
return this._setKVNode(key, value)?.node;
|
|
561
419
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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;
|
|
567
433
|
}
|
|
568
|
-
|
|
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;
|
|
569
436
|
}
|
|
570
437
|
|
|
571
438
|
/**
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
* This is a performance-oriented helper used by boundary fast paths and hinted insertion.
|
|
575
|
-
* It will:
|
|
576
|
-
* - wire parent/child pointers (using accessors, so parent pointers are updated)
|
|
577
|
-
* - initialize children to NIL
|
|
578
|
-
* - mark the new node RED, then run insert fix-up
|
|
579
|
-
*
|
|
580
|
-
* Precondition: the chosen slot (parent.left/parent.right) is empty (NIL/null/undefined).
|
|
439
|
+
* Boolean wrapper for setWithHintNode.
|
|
581
440
|
* @remarks Time O(log n) average, Space O(1)
|
|
582
441
|
*/
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
node.parent = parent;
|
|
586
|
-
if (side === 'left') parent.left = node;
|
|
587
|
-
else parent.right = node;
|
|
588
|
-
node.left = NIL;
|
|
589
|
-
node.right = NIL;
|
|
590
|
-
node.color = 'RED';
|
|
591
|
-
this._updateCountAlongPath(node);
|
|
592
|
-
this._insertFixup(node);
|
|
593
|
-
if (this.isRealNode(this._root)) this._root.color = 'BLACK';
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
/**
|
|
597
|
-
* (Internal) a single source of truth for min/max is header._left/_right.
|
|
598
|
-
* Keep legacy _minNode/_maxNode mirrored for compatibility.
|
|
599
|
-
* @remarks Time O(1), Space O(1)
|
|
600
|
-
*/
|
|
601
|
-
/**
|
|
602
|
-
* (Internal) Update min cache pointers (header._left is the canonical min pointer).
|
|
603
|
-
* @remarks Time O(1), Space O(1)
|
|
604
|
-
*/
|
|
605
|
-
protected _setMinCache(node: RedBlackTreeNode<K, V> | undefined): void {
|
|
606
|
-
this._minNode = node;
|
|
607
|
-
this._header._left = node ?? this.NIL;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
/**
|
|
611
|
-
* (Internal) Update max cache pointers (header._right is the canonical max pointer).
|
|
612
|
-
* @remarks Time O(1), Space O(1)
|
|
613
|
-
*/
|
|
614
|
-
protected _setMaxCache(node: RedBlackTreeNode<K, V> | undefined): void {
|
|
615
|
-
this._maxNode = node;
|
|
616
|
-
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;
|
|
617
444
|
}
|
|
618
445
|
|
|
619
446
|
/**
|
|
620
|
-
*
|
|
447
|
+
* Insert or update a key/value (map mode) or key-only (set mode).
|
|
621
448
|
*
|
|
622
|
-
*
|
|
623
|
-
* -
|
|
624
|
-
* -
|
|
625
|
-
* - 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)
|
|
626
452
|
*
|
|
627
|
-
* Return value:
|
|
628
|
-
* - `{ node, created:false }` when an existing key is updated
|
|
629
|
-
* - `{ node, created:true }` when a new node is inserted
|
|
630
|
-
* - `undefined` only on unexpected internal failure.
|
|
631
453
|
* @remarks Time O(log n) average, Space O(1)
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
const minL = minN.left;
|
|
650
|
-
if (cMin < 0 && (minL === NIL || minL === null || minL === undefined)) {
|
|
651
|
-
const newNode = this.createNode(key, nextValue);
|
|
652
|
-
this._attachNewNode(minN, 'left', newNode);
|
|
653
|
-
if (this._isMapMode) this._store.set(newNode.key, newNode);
|
|
654
|
-
this._size++;
|
|
655
|
-
this._setMinCache(newNode);
|
|
656
|
-
// If max is not initialized yet (tree had 0/1 nodes), mirror max too.
|
|
657
|
-
if (header._right === NIL) this._setMaxCache(newNode);
|
|
658
|
-
return { node: newNode, created: true };
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// Only touch max when key is not less than min.
|
|
662
|
-
if (cMin > 0) {
|
|
663
|
-
const maxN = header._right ?? NIL;
|
|
664
|
-
// Boundary attach: if key is greater than current max and max has no right child.
|
|
665
|
-
const cMax = comparator(key, maxN.key);
|
|
666
|
-
if (cMax === 0) {
|
|
667
|
-
maxN.value = nextValue as V;
|
|
668
|
-
if (this._isMapMode) this._store.set(key, maxN);
|
|
669
|
-
return { node: maxN, created: false };
|
|
670
|
-
}
|
|
671
|
-
const maxR = maxN.right;
|
|
672
|
-
if (cMax > 0 && (maxR === NIL || maxR === null || maxR === undefined)) {
|
|
673
|
-
const newNode = this.createNode(key, nextValue);
|
|
674
|
-
this._attachNewNode(maxN, 'right', newNode);
|
|
675
|
-
if (this._isMapMode) this._store.set(newNode.key, newNode);
|
|
676
|
-
this._size++;
|
|
677
|
-
this._setMaxCache(newNode);
|
|
678
|
-
if (header._left === NIL) this._setMinCache(newNode);
|
|
679
|
-
return { node: newNode, created: true };
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// Normal path: single-pass search + insert/update (avoid double-walking the tree).
|
|
685
|
-
const cmp = comparator;
|
|
686
|
-
const isMapMode = this._isMapMode;
|
|
687
|
-
const store = this._store;
|
|
688
|
-
let current = this._header.parent ?? NIL;
|
|
689
|
-
let parent: RedBlackTreeNode<K, V> | undefined;
|
|
690
|
-
let lastCompared = 0;
|
|
691
|
-
|
|
692
|
-
while (current !== NIL) {
|
|
693
|
-
parent = current;
|
|
694
|
-
lastCompared = cmp(key, current.key);
|
|
695
|
-
if (lastCompared < 0) current = current.left ?? NIL;
|
|
696
|
-
else if (lastCompared > 0) current = current.right ?? NIL;
|
|
697
|
-
else {
|
|
698
|
-
// Update existing.
|
|
699
|
-
current.value = nextValue as V;
|
|
700
|
-
if (isMapMode) store.set(key, current);
|
|
701
|
-
return { node: current, created: false };
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
// Insert new.
|
|
706
|
-
const newNode = this.createNode(key, nextValue);
|
|
707
|
-
// createNode always returns a real node in RedBlackTree.
|
|
708
|
-
newNode.parent = parent;
|
|
709
|
-
|
|
710
|
-
if (!parent) {
|
|
711
|
-
this._setRoot(newNode);
|
|
712
|
-
} else if (lastCompared < 0) {
|
|
713
|
-
parent.left = newNode;
|
|
714
|
-
} else {
|
|
715
|
-
parent.right = newNode;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
newNode.left = NIL;
|
|
719
|
-
newNode.right = NIL;
|
|
720
|
-
newNode.color = 'RED';
|
|
721
|
-
|
|
722
|
-
this._updateCountAlongPath(newNode);
|
|
723
|
-
this._insertFixup(newNode);
|
|
724
|
-
if (this.isRealNode(this._root)) this._root.color = 'BLACK';
|
|
725
|
-
else return undefined;
|
|
726
|
-
|
|
727
|
-
if (isMapMode) store.set(newNode.key, newNode);
|
|
728
|
-
this._size++;
|
|
729
|
-
|
|
730
|
-
// Maintain min/max caches on insertion (header.left/right are canonical).
|
|
731
|
-
const hMin = this._header._left ?? NIL;
|
|
732
|
-
const hMax = this._header._right ?? NIL;
|
|
733
|
-
|
|
734
|
-
// Fast-path: empty tree or attaching directly to an extreme.
|
|
735
|
-
if (hMin === NIL || hMax === NIL) {
|
|
736
|
-
this._setMinCache(newNode);
|
|
737
|
-
this._setMaxCache(newNode);
|
|
738
|
-
} else /* istanbul ignore next -- boundary fast-paths at top of _setKVNode intercept boundary inserts before normal path */ if (parent === hMax && lastCompared > 0) {
|
|
739
|
-
/* istanbul ignore next */ this._setMaxCache(newNode);
|
|
740
|
-
} else /* istanbul ignore next */ if (parent === hMin && lastCompared < 0) {
|
|
741
|
-
/* istanbul ignore next */ this._setMinCache(newNode);
|
|
742
|
-
} else {
|
|
743
|
-
if (cmp(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
744
|
-
if (cmp(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
return { node: newNode, created: true };
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
/**
|
|
751
|
-
* (Internal) Boolean wrapper around `_setKVNode`.
|
|
752
|
-
*
|
|
753
|
-
* Includes a map-mode update fast-path:
|
|
754
|
-
* - If `isMapMode=true` and the key already exists in `_store`, then updating the value does not
|
|
755
|
-
* require any tree search/rotation (tree shape depends only on key).
|
|
756
|
-
* - This path is intentionally limited to `nextValue !== undefined` to preserve existing
|
|
757
|
-
* semantics for `undefined` values.
|
|
758
|
-
* @remarks Time O(log n) average, Space O(1)
|
|
759
|
-
*/
|
|
760
|
-
protected _setKV(key: K, nextValue?: V): boolean {
|
|
761
|
-
if (this._isMapMode) {
|
|
762
|
-
const store = this._store;
|
|
763
|
-
const node = store.get(key);
|
|
764
|
-
if (node) {
|
|
765
|
-
node.value = nextValue as V;
|
|
766
|
-
return true;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
return this._setKVNode(key, nextValue) !== undefined;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
/**
|
|
774
|
-
* Insert/update using a hint node to speed up nearby insertions.
|
|
775
|
-
*
|
|
776
|
-
* close to the expected insertion position (often the previously returned node in a loop).
|
|
777
|
-
*
|
|
778
|
-
* When the hint is a good fit (sorted / nearly-sorted insertion), this can avoid most of the
|
|
779
|
-
* normal root-to-leaf search and reduce constant factors.
|
|
780
|
-
*
|
|
781
|
-
* When the hint does not match (random workloads), this will fall back to the normal set path.
|
|
782
|
-
* @remarks Time O(log n) average, Space O(1)
|
|
783
|
-
*/
|
|
784
|
-
setWithHintNode(key: K, value: V, hint?: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
|
|
785
|
-
if (!hint || !this.isRealNode(hint)) {
|
|
786
|
-
return this._setKVNode(key, value)?.node;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
const cmp = this._compare.bind(this);
|
|
790
|
-
const c0 = cmp(key, hint.key);
|
|
791
|
-
if (c0 === 0) {
|
|
792
|
-
hint.value = value;
|
|
793
|
-
if (this._isMapMode) this._store.set(key, hint);
|
|
794
|
-
return hint;
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
if (c0 < 0) {
|
|
798
|
-
// Ultra-fast path: direct attach if the target slot is empty.
|
|
799
|
-
if (!this.isRealNode(hint.left)) {
|
|
800
|
-
const newNode = this.createNode(key, value);
|
|
801
|
-
if (!this.isRealNode(newNode)) return undefined;
|
|
802
|
-
this._attachNewNode(hint, 'left', newNode);
|
|
803
|
-
if (this._isMapMode) this._store.set(key, newNode);
|
|
804
|
-
this._size++;
|
|
805
|
-
// Maintain header/min/max caches.
|
|
806
|
-
const NIL = this.NIL;
|
|
807
|
-
const hMin = this._header._left ?? NIL;
|
|
808
|
-
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
809
|
-
const hMax = this._header._right ?? NIL;
|
|
810
|
-
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
811
|
-
return newNode;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
const pred = this._predecessorOf(hint);
|
|
815
|
-
if (pred && cmp(pred.key, key) >= 0) {
|
|
816
|
-
return this._setKVNode(key, value)?.node;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
// Try attach as right of pred.
|
|
820
|
-
if (pred && !this.isRealNode(pred.right)) {
|
|
821
|
-
const newNode = this.createNode(key, value);
|
|
822
|
-
if (!this.isRealNode(newNode)) return undefined;
|
|
823
|
-
this._attachNewNode(pred, 'right', newNode);
|
|
824
|
-
if (this._isMapMode) this._store.set(key, newNode);
|
|
825
|
-
this._size++;
|
|
826
|
-
// Maintain header/min/max caches.
|
|
827
|
-
const NIL = this.NIL;
|
|
828
|
-
const hMin = this._header._left ?? NIL;
|
|
829
|
-
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
830
|
-
const hMax = this._header._right ?? NIL;
|
|
831
|
-
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
832
|
-
return newNode;
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
/* istanbul ignore next -- structurally unreachable: predecessor never has a right child (it's the max of left subtree) */
|
|
836
|
-
return this._setKVNode(key, value)?.node;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
// c0 > 0
|
|
840
|
-
// Ultra-fast path: direct attach if the target slot is empty.
|
|
841
|
-
if (!this.isRealNode(hint.right)) {
|
|
842
|
-
const newNode = this.createNode(key, value);
|
|
843
|
-
if (!this.isRealNode(newNode)) return undefined;
|
|
844
|
-
this._attachNewNode(hint, 'right', newNode);
|
|
845
|
-
if (this._isMapMode) this._store.set(key, newNode);
|
|
846
|
-
this._size++;
|
|
847
|
-
// Maintain header/min/max caches.
|
|
848
|
-
const NIL = this.NIL;
|
|
849
|
-
const hMin = this._header._left ?? NIL;
|
|
850
|
-
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
851
|
-
const hMax = this._header._right ?? NIL;
|
|
852
|
-
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
853
|
-
return newNode;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
const succ = this._successorOf(hint);
|
|
857
|
-
if (succ && cmp(succ.key, key) <= 0) {
|
|
858
|
-
return this._setKVNode(key, value)?.node;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
if (succ && !this.isRealNode(succ.left)) {
|
|
862
|
-
const newNode = this.createNode(key, value);
|
|
863
|
-
if (!this.isRealNode(newNode)) return undefined;
|
|
864
|
-
this._attachNewNode(succ, 'left', newNode);
|
|
865
|
-
if (this._isMapMode) this._store.set(key, newNode);
|
|
866
|
-
this._size++;
|
|
867
|
-
// Maintain header/min/max caches.
|
|
868
|
-
const NIL = this.NIL;
|
|
869
|
-
const hMin = this._header._left ?? NIL;
|
|
870
|
-
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
871
|
-
const hMax = this._header._right ?? NIL;
|
|
872
|
-
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
873
|
-
return newNode;
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
/* istanbul ignore next -- structurally unreachable: successor never has a left child (it's the min of right subtree) */
|
|
877
|
-
return this._setKVNode(key, value)?.node;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
/**
|
|
881
|
-
* Boolean wrapper for setWithHintNode.
|
|
882
|
-
* @remarks Time O(log n) average, Space O(1)
|
|
883
|
-
*/
|
|
884
|
-
setWithHint(key: K, value: V, hint?: RedBlackTreeNode<K, V>): boolean {
|
|
885
|
-
return this.setWithHintNode(key, value, hint) !== undefined;
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
/**
|
|
889
|
-
* Insert or update a key/value (map mode) or key-only (set mode).
|
|
890
|
-
*
|
|
891
|
-
* This method is optimized for:
|
|
892
|
-
* - monotonic inserts via min/max boundary fast paths
|
|
893
|
-
* - updates via a single-pass search (no double walk)
|
|
894
|
-
*
|
|
895
|
-
* @remarks Time O(log n) average, Space O(1)
|
|
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
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
* @example
|
|
1057
|
-
* // basic Red-Black Tree with simple number keys
|
|
1058
|
-
* // Create a simple Red-Black Tree with numeric keys
|
|
1059
|
-
* const tree = new RedBlackTree([5, 2, 8, 1, 9]);
|
|
1060
|
-
*
|
|
1061
|
-
* tree.print();
|
|
1062
|
-
* // _2___
|
|
1063
|
-
* // / \
|
|
1064
|
-
* // 1 _8_
|
|
1065
|
-
* // / \
|
|
1066
|
-
* // 5 9
|
|
1067
|
-
*
|
|
1068
|
-
* // Verify the tree maintains sorted order
|
|
1069
|
-
* console.log([...tree.keys()]); // [1, 2, 5, 8, 9];
|
|
1070
|
-
*
|
|
1071
|
-
* // Check size
|
|
1072
|
-
* 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;
|
|
1073
471
|
*/
|
|
1074
472
|
override set(
|
|
1075
473
|
keyNodeOrEntry: K | RedBlackTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
|
|
@@ -1078,24 +476,19 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1078
476
|
// Common path: tree.set(key, value) or tree.set([key, value]).
|
|
1079
477
|
if (!this.isNode(keyNodeOrEntry)) {
|
|
1080
478
|
if (keyNodeOrEntry === null || keyNodeOrEntry === undefined) return false;
|
|
1081
|
-
|
|
1082
479
|
if (this.isEntry(keyNodeOrEntry)) {
|
|
1083
480
|
const key = keyNodeOrEntry[0];
|
|
1084
481
|
if (key === null || key === undefined) return false;
|
|
1085
482
|
const nextValue = value ?? keyNodeOrEntry[1];
|
|
1086
483
|
return this._setKV(key, nextValue);
|
|
1087
484
|
}
|
|
1088
|
-
|
|
1089
485
|
// key-only
|
|
1090
486
|
return this._setKV(keyNodeOrEntry, value);
|
|
1091
487
|
}
|
|
1092
|
-
|
|
1093
488
|
// Node insertion path (advanced usage)
|
|
1094
489
|
const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
|
1095
490
|
if (!this.isRealNode(newNode)) return false;
|
|
1096
|
-
|
|
1097
491
|
const insertStatus = this._insert(newNode);
|
|
1098
|
-
|
|
1099
492
|
if (insertStatus === 'CREATED') {
|
|
1100
493
|
if (this.isRealNode(this._root)) {
|
|
1101
494
|
this._root.color = 'BLACK';
|
|
@@ -1131,198 +524,34 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1131
524
|
* @remarks Time O(log n) average, Space O(1)
|
|
1132
525
|
* @param keyNodeEntryRawOrPredicate - Key, node, or [key, value] entry identifying the node to delete.
|
|
1133
526
|
* @returns Array with deletion metadata (removed node, rebalancing hint if any).
|
|
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
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
* @example
|
|
1297
|
-
* // Remove and rebalance
|
|
1298
|
-
* const rbt = new RedBlackTree<number>([10, 5, 15, 3, 7]);
|
|
1299
|
-
* rbt.delete(5);
|
|
1300
|
-
* console.log(rbt.has(5)); // false;
|
|
1301
|
-
* 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;
|
|
1302
533
|
*/
|
|
1303
534
|
override delete(
|
|
1304
535
|
keyNodeEntryRawOrPredicate: BTNRep<K, V, RedBlackTreeNode<K, V>> | NodePredicate<RedBlackTreeNode<K, V> | null>
|
|
1305
536
|
): boolean {
|
|
1306
537
|
if (keyNodeEntryRawOrPredicate === null) return false;
|
|
1307
|
-
|
|
1308
538
|
let nodeToDelete: OptNode<RedBlackTreeNode<K, V>>;
|
|
1309
539
|
if (this._isPredicate(keyNodeEntryRawOrPredicate)) nodeToDelete = this.getNode(keyNodeEntryRawOrPredicate);
|
|
1310
|
-
else
|
|
1311
|
-
|
|
540
|
+
else
|
|
541
|
+
nodeToDelete = this.isRealNode(keyNodeEntryRawOrPredicate)
|
|
542
|
+
? keyNodeEntryRawOrPredicate
|
|
543
|
+
: this.getNode(keyNodeEntryRawOrPredicate);
|
|
1312
544
|
if (!nodeToDelete) {
|
|
1313
545
|
return false;
|
|
1314
546
|
}
|
|
1315
|
-
|
|
1316
547
|
// Track min/max cache updates before structural modifications.
|
|
1317
548
|
const willDeleteMin = nodeToDelete === this._minNode;
|
|
1318
549
|
const willDeleteMax = nodeToDelete === this._maxNode;
|
|
1319
550
|
const nextMin = willDeleteMin ? this._successorOf(nodeToDelete) : undefined;
|
|
1320
551
|
const nextMax = willDeleteMax ? this._predecessorOf(nodeToDelete) : undefined;
|
|
1321
|
-
|
|
1322
552
|
let originalColor = nodeToDelete.color;
|
|
1323
553
|
const NIL = this.NIL;
|
|
1324
554
|
let replacementNode: RedBlackTreeNode<K, V> = NIL;
|
|
1325
|
-
|
|
1326
555
|
if (!this.isRealNode(nodeToDelete.left)) {
|
|
1327
556
|
// No real left child → replace with right (may be NIL)
|
|
1328
557
|
replacementNode = nodeToDelete.right ?? NIL;
|
|
@@ -1337,7 +566,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1337
566
|
if (successor) {
|
|
1338
567
|
originalColor = successor.color;
|
|
1339
568
|
replacementNode = successor.right ?? NIL;
|
|
1340
|
-
|
|
1341
569
|
if (successor.parent === nodeToDelete) {
|
|
1342
570
|
// Even if replacementNode is NIL, set its parent for fixup
|
|
1343
571
|
replacementNode.parent = successor;
|
|
@@ -1348,7 +576,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1348
576
|
successor.right.parent = successor;
|
|
1349
577
|
}
|
|
1350
578
|
}
|
|
1351
|
-
|
|
1352
579
|
this._transplant(nodeToDelete, successor);
|
|
1353
580
|
successor.left = nodeToDelete.left;
|
|
1354
581
|
if (successor.left) {
|
|
@@ -1359,10 +586,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1359
586
|
}
|
|
1360
587
|
if (this._isMapMode) this._store.delete(nodeToDelete.key);
|
|
1361
588
|
this._size--;
|
|
1362
|
-
|
|
1363
589
|
// Update order-statistic counts from replacement up to root
|
|
1364
|
-
this._updateCountAlongPath(replacementNode?.parent as RedBlackTreeNode<K, V> | undefined ?? replacementNode);
|
|
1365
|
-
|
|
590
|
+
this._updateCountAlongPath((replacementNode?.parent as RedBlackTreeNode<K, V> | undefined) ?? replacementNode);
|
|
1366
591
|
// Update min/max caches.
|
|
1367
592
|
if (this._size <= 0) {
|
|
1368
593
|
this._setMinCache(undefined);
|
|
@@ -1378,346 +603,319 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1378
603
|
this._setMaxCache(this.isRealNode(this._root) ? this.getRightMost(n => n, this._root) : undefined);
|
|
1379
604
|
}
|
|
1380
605
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
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;
|
|
1384
726
|
}
|
|
727
|
+
return p;
|
|
728
|
+
}
|
|
1385
729
|
|
|
1386
|
-
|
|
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';
|
|
1387
753
|
}
|
|
1388
754
|
|
|
1389
755
|
/**
|
|
1390
|
-
*
|
|
1391
|
-
*
|
|
1392
|
-
* @
|
|
1393
|
-
* @template MV
|
|
1394
|
-
* @template MR
|
|
1395
|
-
* @param callback - Mapping function from (key, value, index, tree) to a new [key, value].
|
|
1396
|
-
* @param [options] - See parameter type for details.
|
|
1397
|
-
* @param [thisArg] - See parameter type for details.
|
|
1398
|
-
* @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)
|
|
1399
759
|
*/
|
|
1400
760
|
/**
|
|
1401
|
-
*
|
|
1402
|
-
*
|
|
1403
|
-
* @remarks Time O(N), Space O(N)
|
|
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
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
* @example
|
|
1523
|
-
* // Rebalance tree
|
|
1524
|
-
* const rbt = new RedBlackTree<number>([1, 2, 3, 4, 5]);
|
|
1525
|
-
* rbt.perfectlyBalance();
|
|
1526
|
-
* 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)
|
|
1527
763
|
*/
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
for (const [key, value] of this) entries.push([key, value]);
|
|
1532
|
-
if (entries.length <= 1) return true;
|
|
1533
|
-
this.clear();
|
|
1534
|
-
this.setMany(
|
|
1535
|
-
entries.map(([k]) => k),
|
|
1536
|
-
entries.map(([, v]) => v),
|
|
1537
|
-
true // isBalanceAdd
|
|
1538
|
-
);
|
|
1539
|
-
return true;
|
|
764
|
+
protected _setMinCache(node: RedBlackTreeNode<K, V> | undefined): void {
|
|
765
|
+
this._minNode = node;
|
|
766
|
+
this._header._left = node ?? this.NIL;
|
|
1540
767
|
}
|
|
1541
768
|
|
|
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
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
* @example
|
|
1704
|
-
* // Transform to new tree
|
|
1705
|
-
* const rbt = new RedBlackTree<number, number>([[1, 10], [2, 20]]);
|
|
1706
|
-
* const doubled = rbt.map((v, k) => [k, (v ?? 0) * 2] as [number, number]);
|
|
1707
|
-
* 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)
|
|
1708
772
|
*/
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
): RedBlackTree<MK, MV, MR> {
|
|
1714
|
-
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
|
+
}
|
|
1715
777
|
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
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
|
+
}
|
|
1719
839
|
}
|
|
1720
|
-
|
|
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;
|
|
1721
919
|
}
|
|
1722
920
|
|
|
1723
921
|
/**
|
|
@@ -1773,7 +971,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1773
971
|
newNode: RedBlackTreeNode<K, V>
|
|
1774
972
|
): RedBlackTreeNode<K, V> {
|
|
1775
973
|
newNode.color = oldNode.color;
|
|
1776
|
-
|
|
1777
974
|
return super._replaceNode(oldNode, newNode);
|
|
1778
975
|
}
|
|
1779
976
|
|
|
@@ -1786,11 +983,9 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1786
983
|
protected _insert(node: RedBlackTreeNode<K, V>): CRUD {
|
|
1787
984
|
const NIL = this.NIL;
|
|
1788
985
|
const cmp = this._compare.bind(this);
|
|
1789
|
-
|
|
1790
986
|
let current = this._header.parent ?? NIL;
|
|
1791
987
|
let parent: RedBlackTreeNode<K, V> | undefined;
|
|
1792
988
|
let lastCompared = 0;
|
|
1793
|
-
|
|
1794
989
|
while (current !== NIL) {
|
|
1795
990
|
parent = current;
|
|
1796
991
|
lastCompared = cmp(node.key, current.key);
|
|
@@ -1803,9 +998,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1803
998
|
return 'UPDATED';
|
|
1804
999
|
}
|
|
1805
1000
|
}
|
|
1806
|
-
|
|
1807
1001
|
node.parent = parent;
|
|
1808
|
-
|
|
1809
1002
|
if (!parent) {
|
|
1810
1003
|
this._setRoot(node);
|
|
1811
1004
|
} else if (lastCompared < 0) {
|
|
@@ -1813,14 +1006,11 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1813
1006
|
} else {
|
|
1814
1007
|
parent.right = node;
|
|
1815
1008
|
}
|
|
1816
|
-
|
|
1817
1009
|
node.left = NIL;
|
|
1818
1010
|
node.right = NIL;
|
|
1819
1011
|
node.color = 'RED';
|
|
1820
|
-
|
|
1821
1012
|
// Update counts along insertion path before fixup
|
|
1822
1013
|
this._updateCountAlongPath(node);
|
|
1823
|
-
|
|
1824
1014
|
this._insertFixup(node);
|
|
1825
1015
|
return 'CREATED';
|
|
1826
1016
|
}
|
|
@@ -1840,7 +1030,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1840
1030
|
} else {
|
|
1841
1031
|
u.parent.right = v;
|
|
1842
1032
|
}
|
|
1843
|
-
|
|
1844
1033
|
if (v) {
|
|
1845
1034
|
v.parent = u.parent;
|
|
1846
1035
|
}
|
|
@@ -1855,14 +1044,11 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1855
1044
|
protected _insertFixup(z: RedBlackTreeNode<K, V> | undefined): void {
|
|
1856
1045
|
const leftRotate = this._leftRotate.bind(this);
|
|
1857
1046
|
const rightRotate = this._rightRotate.bind(this);
|
|
1858
|
-
|
|
1859
1047
|
while (z) {
|
|
1860
1048
|
const p = z.parent;
|
|
1861
1049
|
if (!p || p.color !== 'RED') break;
|
|
1862
|
-
|
|
1863
1050
|
const gp = p.parent;
|
|
1864
1051
|
if (!gp) break;
|
|
1865
|
-
|
|
1866
1052
|
if (p === gp.left) {
|
|
1867
1053
|
const y = gp.right;
|
|
1868
1054
|
if (y?.color === 'RED') {
|
|
@@ -1872,12 +1058,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1872
1058
|
z = gp;
|
|
1873
1059
|
continue;
|
|
1874
1060
|
}
|
|
1875
|
-
|
|
1876
1061
|
if (z === p.right) {
|
|
1877
1062
|
z = p;
|
|
1878
1063
|
leftRotate(z);
|
|
1879
1064
|
}
|
|
1880
|
-
|
|
1881
1065
|
const p2 = z?.parent;
|
|
1882
1066
|
const gp2 = p2?.parent;
|
|
1883
1067
|
if (p2 && gp2) {
|
|
@@ -1894,12 +1078,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1894
1078
|
z = gp;
|
|
1895
1079
|
continue;
|
|
1896
1080
|
}
|
|
1897
|
-
|
|
1898
1081
|
if (z === p.left) {
|
|
1899
1082
|
z = p;
|
|
1900
1083
|
rightRotate(z);
|
|
1901
1084
|
}
|
|
1902
|
-
|
|
1903
1085
|
const p2 = z?.parent;
|
|
1904
1086
|
const gp2 = p2?.parent;
|
|
1905
1087
|
if (p2 && gp2) {
|
|
@@ -1908,10 +1090,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1908
1090
|
leftRotate(gp2);
|
|
1909
1091
|
}
|
|
1910
1092
|
}
|
|
1911
|
-
|
|
1912
1093
|
break;
|
|
1913
1094
|
}
|
|
1914
|
-
|
|
1915
1095
|
if (this.isRealNode(this._root)) this._root.color = 'BLACK';
|
|
1916
1096
|
}
|
|
1917
1097
|
|
|
@@ -1926,17 +1106,13 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1926
1106
|
// `node` is the child that replaced the deleted node (may be NIL sentinel).
|
|
1927
1107
|
// If RED → just recolor BLACK (trivial fix). If BLACK → double-black repair.
|
|
1928
1108
|
if (!node) return;
|
|
1929
|
-
|
|
1930
1109
|
const NIL = this.NIL;
|
|
1931
1110
|
let current: RedBlackTreeNode<K, V> = node;
|
|
1932
|
-
|
|
1933
1111
|
while (current !== this.root && current.color === 'BLACK') {
|
|
1934
1112
|
const parent: RedBlackTreeNode<K, V> | undefined = current.parent;
|
|
1935
1113
|
if (!parent) break;
|
|
1936
|
-
|
|
1937
1114
|
const nodeIsLeft = current === parent.left;
|
|
1938
1115
|
let sibling = nodeIsLeft ? parent.right : parent.left;
|
|
1939
|
-
|
|
1940
1116
|
// Case 1: sibling is RED → rotate to get a BLACK sibling
|
|
1941
1117
|
if (sibling && sibling.color === 'RED') {
|
|
1942
1118
|
sibling.color = 'BLACK';
|
|
@@ -1949,12 +1125,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1949
1125
|
sibling = parent.left;
|
|
1950
1126
|
}
|
|
1951
1127
|
}
|
|
1952
|
-
|
|
1953
1128
|
const sibLeft = sibling?.left;
|
|
1954
1129
|
const sibRight = sibling?.right;
|
|
1955
1130
|
const sibLeftBlack = !sibLeft || sibLeft === NIL || sibLeft.color === 'BLACK';
|
|
1956
1131
|
const sibRightBlack = !sibRight || sibRight === NIL || sibRight.color === 'BLACK';
|
|
1957
|
-
|
|
1958
1132
|
if (sibLeftBlack && sibRightBlack) {
|
|
1959
1133
|
// Case 2: sibling's children are both BLACK → recolor sibling RED, move up
|
|
1960
1134
|
if (sibling) sibling.color = 'RED';
|
|
@@ -1990,7 +1164,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1990
1164
|
current = this.root!;
|
|
1991
1165
|
}
|
|
1992
1166
|
}
|
|
1993
|
-
|
|
1994
1167
|
current.color = 'BLACK';
|
|
1995
1168
|
}
|
|
1996
1169
|
|
|
@@ -2004,16 +1177,12 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
2004
1177
|
if (!x || !x.right) {
|
|
2005
1178
|
return;
|
|
2006
1179
|
}
|
|
2007
|
-
|
|
2008
1180
|
const y = x.right;
|
|
2009
1181
|
x.right = y.left;
|
|
2010
|
-
|
|
2011
1182
|
if (y.left && y.left !== this.NIL) {
|
|
2012
1183
|
y.left.parent = x;
|
|
2013
1184
|
}
|
|
2014
|
-
|
|
2015
1185
|
y.parent = x.parent;
|
|
2016
|
-
|
|
2017
1186
|
if (!x.parent) {
|
|
2018
1187
|
this._setRoot(y);
|
|
2019
1188
|
} else if (x === x.parent.left) {
|
|
@@ -2021,10 +1190,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
2021
1190
|
} else {
|
|
2022
1191
|
x.parent.right = y;
|
|
2023
1192
|
}
|
|
2024
|
-
|
|
2025
1193
|
y.left = x;
|
|
2026
1194
|
x.parent = y;
|
|
2027
|
-
|
|
2028
1195
|
// Update counts: x first (now child), then y (now parent)
|
|
2029
1196
|
this._updateCount(x);
|
|
2030
1197
|
this._updateCount(y);
|
|
@@ -2040,16 +1207,12 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
2040
1207
|
if (!y || !y.left) {
|
|
2041
1208
|
return;
|
|
2042
1209
|
}
|
|
2043
|
-
|
|
2044
1210
|
const x = y.left;
|
|
2045
1211
|
y.left = x.right;
|
|
2046
|
-
|
|
2047
1212
|
if (x.right && x.right !== this.NIL) {
|
|
2048
1213
|
x.right.parent = y;
|
|
2049
1214
|
}
|
|
2050
|
-
|
|
2051
1215
|
x.parent = y.parent;
|
|
2052
|
-
|
|
2053
1216
|
if (!y.parent) {
|
|
2054
1217
|
this._setRoot(x);
|
|
2055
1218
|
} else if (y === y.parent.left) {
|
|
@@ -2057,10 +1220,8 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
2057
1220
|
} else {
|
|
2058
1221
|
y.parent.right = x;
|
|
2059
1222
|
}
|
|
2060
|
-
|
|
2061
1223
|
x.right = y;
|
|
2062
1224
|
y.parent = x;
|
|
2063
|
-
|
|
2064
1225
|
// Update counts: y first (now child), then x (now parent)
|
|
2065
1226
|
this._updateCount(y);
|
|
2066
1227
|
this._updateCount(x);
|