max-priority-queue-typed 2.4.0 → 2.4.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/dist/types/data-structures/base/linear-base.d.ts +6 -6
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +6 -6
- package/dist/types/data-structures/binary-tree/bst.d.ts +2 -1
- package/dist/types/data-structures/binary-tree/index.d.ts +3 -3
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +150 -20
- package/dist/types/data-structures/binary-tree/tree-map.d.ts +188 -0
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +238 -147
- package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +270 -0
- package/dist/types/data-structures/binary-tree/tree-set.d.ts +181 -0
- package/dist/types/interfaces/binary-tree.d.ts +2 -2
- package/dist/types/types/data-structures/binary-tree/index.d.ts +3 -3
- package/dist/types/types/data-structures/binary-tree/tree-map.d.ts +33 -0
- package/dist/types/types/data-structures/binary-tree/tree-multi-set.d.ts +16 -0
- package/dist/types/types/data-structures/binary-tree/tree-set.d.ts +33 -0
- package/package.json +2 -2
- package/src/data-structures/base/linear-base.ts +2 -12
- package/src/data-structures/binary-tree/avl-tree.ts +1 -1
- package/src/data-structures/binary-tree/binary-tree.ts +45 -21
- package/src/data-structures/binary-tree/bst.ts +85 -10
- package/src/data-structures/binary-tree/index.ts +3 -3
- package/src/data-structures/binary-tree/red-black-tree.ts +568 -76
- package/src/data-structures/binary-tree/tree-map.ts +439 -0
- package/src/data-structures/binary-tree/tree-multi-map.ts +488 -325
- package/src/data-structures/binary-tree/tree-multi-set.ts +502 -0
- package/src/data-structures/binary-tree/tree-set.ts +407 -0
- package/src/data-structures/queue/deque.ts +10 -0
- package/src/interfaces/binary-tree.ts +2 -2
- package/src/types/data-structures/binary-tree/index.ts +3 -3
- package/src/types/data-structures/binary-tree/tree-map.ts +45 -0
- package/src/types/data-structures/binary-tree/tree-multi-set.ts +19 -0
- package/src/types/data-structures/binary-tree/tree-set.ts +39 -0
- package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +0 -236
- package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +0 -197
- package/dist/types/data-structures/binary-tree/tree-counter.d.ts +0 -243
- package/dist/types/types/data-structures/binary-tree/avl-tree-counter.d.ts +0 -2
- package/dist/types/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +0 -2
- package/dist/types/types/data-structures/binary-tree/tree-counter.d.ts +0 -2
- package/src/data-structures/binary-tree/avl-tree-counter.ts +0 -539
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +0 -438
- package/src/data-structures/binary-tree/tree-counter.ts +0 -575
- package/src/types/data-structures/binary-tree/avl-tree-counter.ts +0 -3
- package/src/types/data-structures/binary-tree/avl-tree-multi-map.ts +0 -3
- package/src/types/data-structures/binary-tree/tree-counter.ts +0 -3
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type {
|
|
10
|
-
BinaryTreeDeleteResult,
|
|
10
|
+
BinaryTreeDeleteResult, BTNRep,
|
|
11
11
|
CRUD,
|
|
12
12
|
EntryCallback,
|
|
13
|
-
FamilyPosition,
|
|
13
|
+
FamilyPosition, NodePredicate,
|
|
14
14
|
OptNode,
|
|
15
15
|
RBTNColor,
|
|
16
16
|
RedBlackTreeOptions
|
|
@@ -24,12 +24,11 @@ export class RedBlackTreeNode<K = any, V = any> {
|
|
|
24
24
|
parent?: RedBlackTreeNode<K, V> = undefined;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Create a Red-Black Tree
|
|
28
|
-
* @remarks Time O(
|
|
29
|
-
* @param key -
|
|
30
|
-
* @param [value]-
|
|
31
|
-
* @param color -
|
|
32
|
-
* @returns New RedBlackTree instance.
|
|
27
|
+
* Create a Red-Black Tree node.
|
|
28
|
+
* @remarks Time O(1), Space O(1)
|
|
29
|
+
* @param key - Node key.
|
|
30
|
+
* @param [value] - Node value (unused in map mode trees).
|
|
31
|
+
* @param color - Node color.
|
|
33
32
|
*/
|
|
34
33
|
|
|
35
34
|
constructor(key: K, value?: V, color: RBTNColor = 'BLACK') {
|
|
@@ -179,7 +178,7 @@ export class RedBlackTreeNode<K = any, V = any> {
|
|
|
179
178
|
|
|
180
179
|
/**
|
|
181
180
|
* Represents a Red-Black Tree (self-balancing BST) supporting map-like mode and stable O(log n) updates.
|
|
182
|
-
* @remarks
|
|
181
|
+
* @remarks Operation complexity depends on the method; see each method's docs.
|
|
183
182
|
* @template K
|
|
184
183
|
* @template V
|
|
185
184
|
* @template R
|
|
@@ -287,10 +286,18 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
287
286
|
> = [],
|
|
288
287
|
options?: RedBlackTreeOptions<K, V, R>
|
|
289
288
|
) {
|
|
289
|
+
|
|
290
290
|
super([], options);
|
|
291
291
|
|
|
292
292
|
this._root = this.NIL;
|
|
293
293
|
|
|
294
|
+
// Not part of the actual tree; used only as an internal cache hub.
|
|
295
|
+
this._header = new RedBlackTreeNode<K, V>(undefined as K, undefined, 'BLACK');
|
|
296
|
+
this._header.parent = this.NIL;
|
|
297
|
+
// Avoid using accessors here: they would set NIL.parent and can corrupt sentinel invariants.
|
|
298
|
+
this._header._left = this.NIL;
|
|
299
|
+
this._header._right = this.NIL;
|
|
300
|
+
|
|
294
301
|
if (keysNodesEntriesOrRaws) {
|
|
295
302
|
this.setMany(keysNodesEntriesOrRaws);
|
|
296
303
|
}
|
|
@@ -298,6 +305,26 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
298
305
|
|
|
299
306
|
protected override _root: RedBlackTreeNode<K, V> | undefined;
|
|
300
307
|
|
|
308
|
+
/**
|
|
309
|
+
* (Internal) Header sentinel:
|
|
310
|
+
* - header.parent -> root
|
|
311
|
+
* - header._left -> min (or NIL)
|
|
312
|
+
* - header._right -> max (or NIL)
|
|
313
|
+
*
|
|
314
|
+
* IMPORTANT:
|
|
315
|
+
* - This header is NOT part of the actual tree.
|
|
316
|
+
* - Do NOT use `header.left` / `header.right` accessors for wiring: those setters update `NIL.parent`
|
|
317
|
+
* and can corrupt sentinel invariants / cause hangs. Only touch `header._left/_right`.
|
|
318
|
+
*/
|
|
319
|
+
protected _header: RedBlackTreeNode<K, V>;
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* (Internal) Cache of the current minimum and maximum nodes.
|
|
323
|
+
* Used for fast-path insert/update when keys are monotonic or near-boundary.
|
|
324
|
+
*/
|
|
325
|
+
protected _minNode: RedBlackTreeNode<K, V> | undefined;
|
|
326
|
+
protected _maxNode: RedBlackTreeNode<K, V> | undefined;
|
|
327
|
+
|
|
301
328
|
/**
|
|
302
329
|
* Get the current root node.
|
|
303
330
|
* @remarks Time O(1), Space O(1)
|
|
@@ -316,7 +343,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
316
343
|
* @returns A new RedBlackTreeNode instance.
|
|
317
344
|
*/
|
|
318
345
|
override createNode(key: K, value?: V, color: RBTNColor = 'BLACK'): RedBlackTreeNode<K, V> {
|
|
319
|
-
return new RedBlackTreeNode<K, V>(key,
|
|
346
|
+
return new RedBlackTreeNode<K, V>(key, value, color);
|
|
320
347
|
}
|
|
321
348
|
|
|
322
349
|
/**
|
|
@@ -338,22 +365,421 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
338
365
|
* @returns void
|
|
339
366
|
*/
|
|
340
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Remove all nodes and clear internal caches.
|
|
370
|
+
* @remarks Time O(n) average, Space O(1)
|
|
371
|
+
*/
|
|
341
372
|
override clear() {
|
|
342
373
|
super.clear();
|
|
343
374
|
this._root = this.NIL;
|
|
375
|
+
this._header.parent = this.NIL;
|
|
376
|
+
this._setMinCache(undefined);
|
|
377
|
+
this._setMaxCache(undefined);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* (Internal) Find a node by key using a tight BST walk (no allocations).
|
|
383
|
+
*
|
|
384
|
+
* NOTE: This uses `header.parent` as the canonical root pointer.
|
|
385
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
386
|
+
*/
|
|
387
|
+
protected _findNodeByKey(key: K): RedBlackTreeNode<K, V> | undefined {
|
|
388
|
+
const NIL = this.NIL;
|
|
389
|
+
const cmp = this._compare.bind(this);
|
|
390
|
+
|
|
391
|
+
let cur = (this._header.parent) ?? NIL;
|
|
392
|
+
while (cur !== NIL) {
|
|
393
|
+
const c = cmp(key, cur.key);
|
|
394
|
+
if (c < 0) cur = cur.left ?? NIL;
|
|
395
|
+
else if (c > 0) cur = cur.right ?? NIL;
|
|
396
|
+
else return cur;
|
|
397
|
+
}
|
|
398
|
+
return undefined;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* (Internal) In-order predecessor of a node in a BST.
|
|
403
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
404
|
+
*/
|
|
405
|
+
protected _predecessorOf(node: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
|
|
406
|
+
const NIL = this.NIL;
|
|
407
|
+
if (node.left && node.left !== NIL) {
|
|
408
|
+
let cur = node.left;
|
|
409
|
+
while (cur.right && cur.right !== NIL) cur = cur.right;
|
|
410
|
+
return cur;
|
|
411
|
+
}
|
|
412
|
+
let cur: RedBlackTreeNode<K, V> | undefined = node;
|
|
413
|
+
let p = node.parent;
|
|
414
|
+
while (p && cur === p.left) {
|
|
415
|
+
cur = p;
|
|
416
|
+
p = p.parent;
|
|
417
|
+
}
|
|
418
|
+
return p;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* (Internal) In-order successor of a node in a BST.
|
|
423
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
424
|
+
*/
|
|
425
|
+
protected _successorOf(node: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
|
|
426
|
+
const NIL = this.NIL;
|
|
427
|
+
if (node.right && node.right !== NIL) {
|
|
428
|
+
let cur = node.right;
|
|
429
|
+
while (cur.left && cur.left !== NIL) cur = cur.left;
|
|
430
|
+
return cur;
|
|
431
|
+
}
|
|
432
|
+
let cur: RedBlackTreeNode<K, V> | undefined = node;
|
|
433
|
+
let p = node.parent;
|
|
434
|
+
while (p && cur === p.right) {
|
|
435
|
+
cur = p;
|
|
436
|
+
p = p.parent;
|
|
437
|
+
}
|
|
438
|
+
return p;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* (Internal) Attach a new node directly under a known parent/side (no search).
|
|
443
|
+
*
|
|
444
|
+
* This is a performance-oriented helper used by boundary fast paths and hinted insertion.
|
|
445
|
+
* It will:
|
|
446
|
+
* - wire parent/child pointers (using accessors, so parent pointers are updated)
|
|
447
|
+
* - initialize children to NIL
|
|
448
|
+
* - mark the new node RED, then run insert fix-up
|
|
449
|
+
*
|
|
450
|
+
* Precondition: the chosen slot (parent.left/parent.right) is empty (NIL/null/undefined).
|
|
451
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
452
|
+
*/
|
|
453
|
+
protected _attachNewNode(parent: RedBlackTreeNode<K, V>, side: 'left' | 'right', node: RedBlackTreeNode<K, V>): void {
|
|
454
|
+
const NIL = this.NIL;
|
|
455
|
+
node.parent = parent;
|
|
456
|
+
if (side === 'left') parent.left = node;
|
|
457
|
+
else parent.right = node;
|
|
458
|
+
node.left = NIL;
|
|
459
|
+
node.right = NIL;
|
|
460
|
+
node.color = 'RED';
|
|
461
|
+
this._insertFixup(node);
|
|
462
|
+
if (this.isRealNode(this._root)) this._root.color = 'BLACK';
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* (Internal) a single source of truth for min/max is header._left/_right.
|
|
467
|
+
* Keep legacy _minNode/_maxNode mirrored for compatibility.
|
|
468
|
+
* @remarks Time O(1), Space O(1)
|
|
469
|
+
*/
|
|
470
|
+
/**
|
|
471
|
+
* (Internal) Update min cache pointers (header._left is the canonical min pointer).
|
|
472
|
+
* @remarks Time O(1), Space O(1)
|
|
473
|
+
*/
|
|
474
|
+
protected _setMinCache(node: RedBlackTreeNode<K, V> | undefined): void {
|
|
475
|
+
this._minNode = node;
|
|
476
|
+
this._header._left = node ?? this.NIL;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* (Internal) Update max cache pointers (header._right is the canonical max pointer).
|
|
481
|
+
* @remarks Time O(1), Space O(1)
|
|
482
|
+
*/
|
|
483
|
+
protected _setMaxCache(node: RedBlackTreeNode<K, V> | undefined): void {
|
|
484
|
+
this._maxNode = node;
|
|
485
|
+
this._header._right = node ?? this.NIL;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* (Internal) Core set implementation returning the affected node.
|
|
490
|
+
*
|
|
491
|
+
* Hot path goals:
|
|
492
|
+
* - Avoid double walks (search+insert): do a single traversal that either updates or inserts.
|
|
493
|
+
* - Use header min/max caches to fast-path boundary inserts.
|
|
494
|
+
* - Keep header._left/_right as canonical min/max pointers.
|
|
495
|
+
*
|
|
496
|
+
* Return value:
|
|
497
|
+
* - `{ node, created:false }` when an existing key is updated
|
|
498
|
+
* - `{ node, created:true }` when a new node is inserted
|
|
499
|
+
* - `undefined` only on unexpected internal failure.
|
|
500
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
501
|
+
*/
|
|
502
|
+
protected _setKVNode(key: K, nextValue?: V): { node: RedBlackTreeNode<K, V>; created: boolean } | undefined {
|
|
503
|
+
const NIL = this.NIL;
|
|
504
|
+
const comparator = this._comparator;
|
|
505
|
+
|
|
506
|
+
// Read via header to avoid undefined checks (header uses NIL when empty).
|
|
507
|
+
const header = this._header;
|
|
508
|
+
const minN = header._left ?? NIL;
|
|
509
|
+
if (minN !== NIL) {
|
|
510
|
+
const cMin = comparator(key, minN.key);
|
|
511
|
+
if (cMin === 0) {
|
|
512
|
+
minN.value = nextValue as V;
|
|
513
|
+
if (this._isMapMode) this._store.set(key, minN);
|
|
514
|
+
return { node: minN, created: false };
|
|
515
|
+
}
|
|
516
|
+
// Boundary attach: if key is smaller than current min and min has no left child.
|
|
517
|
+
// Inline NIL/null/undefined check to avoid isRealNode overhead on hot path.
|
|
518
|
+
const minL = minN.left;
|
|
519
|
+
if (cMin < 0 && (minL === NIL || minL === null || minL === undefined)) {
|
|
520
|
+
const newNode = this.createNode(key, nextValue);
|
|
521
|
+
this._attachNewNode(minN, 'left', newNode);
|
|
522
|
+
if (this._isMapMode) this._store.set(newNode.key, newNode);
|
|
523
|
+
this._size++;
|
|
524
|
+
this._setMinCache(newNode);
|
|
525
|
+
// If max is not initialized yet (tree had 0/1 nodes), mirror max too.
|
|
526
|
+
if (header._right === NIL) this._setMaxCache(newNode);
|
|
527
|
+
return { node: newNode, created: true };
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Only touch max when key is not less than min.
|
|
531
|
+
if (cMin > 0) {
|
|
532
|
+
const maxN = header._right ?? NIL;
|
|
533
|
+
// Boundary attach: if key is greater than current max and max has no right child.
|
|
534
|
+
const cMax = comparator(key, maxN.key);
|
|
535
|
+
if (cMax === 0) {
|
|
536
|
+
maxN.value = nextValue as V;
|
|
537
|
+
if (this._isMapMode) this._store.set(key, maxN);
|
|
538
|
+
return { node: maxN, created: false };
|
|
539
|
+
}
|
|
540
|
+
const maxR = maxN.right;
|
|
541
|
+
if (cMax > 0 && (maxR === NIL || maxR === null || maxR === undefined)) {
|
|
542
|
+
const newNode = this.createNode(key, nextValue);
|
|
543
|
+
this._attachNewNode(maxN, 'right', newNode);
|
|
544
|
+
if (this._isMapMode) this._store.set(newNode.key, newNode);
|
|
545
|
+
this._size++;
|
|
546
|
+
this._setMaxCache(newNode);
|
|
547
|
+
if (header._left === NIL) this._setMinCache(newNode);
|
|
548
|
+
return { node: newNode, created: true };
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Normal path: single-pass search + insert/update (avoid double-walking the tree).
|
|
554
|
+
const cmp = comparator;
|
|
555
|
+
const isMapMode = this._isMapMode;
|
|
556
|
+
const store = this._store;
|
|
557
|
+
let current = this._header.parent ?? NIL;
|
|
558
|
+
let parent: RedBlackTreeNode<K, V> | undefined;
|
|
559
|
+
let lastCompared = 0;
|
|
560
|
+
|
|
561
|
+
while (current !== NIL) {
|
|
562
|
+
parent = current;
|
|
563
|
+
lastCompared = cmp(key, current.key);
|
|
564
|
+
if (lastCompared < 0) current = current.left ?? NIL;
|
|
565
|
+
else if (lastCompared > 0) current = current.right ?? NIL;
|
|
566
|
+
else {
|
|
567
|
+
// Update existing.
|
|
568
|
+
current.value = nextValue as V;
|
|
569
|
+
if (isMapMode) store.set(key, current);
|
|
570
|
+
return { node: current, created: false };
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Insert new.
|
|
575
|
+
const newNode = this.createNode(key, nextValue);
|
|
576
|
+
// createNode always returns a real node in RedBlackTree.
|
|
577
|
+
newNode.parent = parent;
|
|
578
|
+
|
|
579
|
+
if (!parent) {
|
|
580
|
+
this._setRoot(newNode);
|
|
581
|
+
} else if (lastCompared < 0) {
|
|
582
|
+
parent.left = newNode;
|
|
583
|
+
} else {
|
|
584
|
+
parent.right = newNode;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
newNode.left = NIL;
|
|
588
|
+
newNode.right = NIL;
|
|
589
|
+
newNode.color = 'RED';
|
|
590
|
+
|
|
591
|
+
this._insertFixup(newNode);
|
|
592
|
+
if (this.isRealNode(this._root)) this._root.color = 'BLACK';
|
|
593
|
+
else return undefined;
|
|
594
|
+
|
|
595
|
+
if (isMapMode) store.set(newNode.key, newNode);
|
|
596
|
+
this._size++;
|
|
597
|
+
|
|
598
|
+
// Maintain min/max caches on insertion (header.left/right are canonical).
|
|
599
|
+
const hMin = this._header._left ?? NIL;
|
|
600
|
+
const hMax = this._header._right ?? NIL;
|
|
601
|
+
|
|
602
|
+
// Fast-path: empty tree or attaching directly to an extreme.
|
|
603
|
+
if (hMin === NIL || hMax === NIL) {
|
|
604
|
+
this._setMinCache(newNode);
|
|
605
|
+
this._setMaxCache(newNode);
|
|
606
|
+
} else if (parent === hMax && lastCompared > 0) {
|
|
607
|
+
this._setMaxCache(newNode);
|
|
608
|
+
} else if (parent === hMin && lastCompared < 0) {
|
|
609
|
+
this._setMinCache(newNode);
|
|
610
|
+
} else {
|
|
611
|
+
if (cmp(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
612
|
+
if (cmp(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
return { node: newNode, created: true };
|
|
344
616
|
}
|
|
345
617
|
|
|
346
618
|
/**
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
*
|
|
619
|
+
* (Internal) Boolean wrapper around `_setKVNode`.
|
|
620
|
+
*
|
|
621
|
+
* Includes a map-mode update fast-path:
|
|
622
|
+
* - If `isMapMode=true` and the key already exists in `_store`, then updating the value does not
|
|
623
|
+
* require any tree search/rotation (tree shape depends only on key).
|
|
624
|
+
* - This path is intentionally limited to `nextValue !== undefined` to preserve existing
|
|
625
|
+
* semantics for `undefined` values.
|
|
626
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
627
|
+
*/
|
|
628
|
+
protected _setKV(key: K, nextValue?: V): boolean {
|
|
629
|
+
if (this._isMapMode) {
|
|
630
|
+
const store = this._store;
|
|
631
|
+
const node = store.get(key);
|
|
632
|
+
if (node) {
|
|
633
|
+
node.value = nextValue as V;
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return this._setKVNode(key, nextValue) !== undefined;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Insert/update using a hint node to speed up nearby insertions.
|
|
643
|
+
*
|
|
644
|
+
* close to the expected insertion position (often the previously returned node in a loop).
|
|
645
|
+
*
|
|
646
|
+
* When the hint is a good fit (sorted / nearly-sorted insertion), this can avoid most of the
|
|
647
|
+
* normal root-to-leaf search and reduce constant factors.
|
|
648
|
+
*
|
|
649
|
+
* When the hint does not match (random workloads), this will fall back to the normal set path.
|
|
650
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
651
|
+
*/
|
|
652
|
+
setWithHintNode(key: K, value: V, hint?: RedBlackTreeNode<K, V>): RedBlackTreeNode<K, V> | undefined {
|
|
653
|
+
if (!hint || !this.isRealNode(hint)) {
|
|
654
|
+
return this._setKVNode(key, value)?.node;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const cmp = this._compare.bind(this);
|
|
658
|
+
const c0 = cmp(key, hint.key);
|
|
659
|
+
if (c0 === 0) {
|
|
660
|
+
hint.value = value;
|
|
661
|
+
if (this._isMapMode) this._store.set(key, hint);
|
|
662
|
+
return hint;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
if (c0 < 0) {
|
|
666
|
+
// Ultra-fast path: direct attach if the target slot is empty.
|
|
667
|
+
if (!this.isRealNode(hint.left)) {
|
|
668
|
+
const newNode = this.createNode(key, value);
|
|
669
|
+
if (!this.isRealNode(newNode)) return undefined;
|
|
670
|
+
this._attachNewNode(hint, 'left', newNode);
|
|
671
|
+
if (this._isMapMode) this._store.set(key, newNode);
|
|
672
|
+
this._size++;
|
|
673
|
+
// Maintain header/min/max caches.
|
|
674
|
+
const NIL = this.NIL;
|
|
675
|
+
const hMin = this._header._left ?? NIL;
|
|
676
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
677
|
+
const hMax = this._header._right ?? NIL;
|
|
678
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
679
|
+
return newNode;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const pred = this._predecessorOf(hint);
|
|
683
|
+
if (pred && cmp(pred.key, key) >= 0) {
|
|
684
|
+
return this._setKVNode(key, value)?.node;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Try attach as right of pred.
|
|
688
|
+
if (pred && !this.isRealNode(pred.right)) {
|
|
689
|
+
const newNode = this.createNode(key, value);
|
|
690
|
+
if (!this.isRealNode(newNode)) return undefined;
|
|
691
|
+
this._attachNewNode(pred, 'right', newNode);
|
|
692
|
+
if (this._isMapMode) this._store.set(key, newNode);
|
|
693
|
+
this._size++;
|
|
694
|
+
// Maintain header/min/max caches.
|
|
695
|
+
const NIL = this.NIL;
|
|
696
|
+
const hMin = this._header._left ?? NIL;
|
|
697
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
698
|
+
const hMax = this._header._right ?? NIL;
|
|
699
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
700
|
+
return newNode;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return this._setKVNode(key, value)?.node;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// c0 > 0
|
|
707
|
+
// Ultra-fast path: direct attach if the target slot is empty.
|
|
708
|
+
if (!this.isRealNode(hint.right)) {
|
|
709
|
+
const newNode = this.createNode(key, value);
|
|
710
|
+
if (!this.isRealNode(newNode)) return undefined;
|
|
711
|
+
this._attachNewNode(hint, 'right', newNode);
|
|
712
|
+
if (this._isMapMode) this._store.set(key, newNode);
|
|
713
|
+
this._size++;
|
|
714
|
+
// Maintain header/min/max caches.
|
|
715
|
+
const NIL = this.NIL;
|
|
716
|
+
const hMin = this._header._left ?? NIL;
|
|
717
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
718
|
+
const hMax = this._header._right ?? NIL;
|
|
719
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
720
|
+
return newNode;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const succ = this._successorOf(hint);
|
|
724
|
+
if (succ && cmp(succ.key, key) <= 0) {
|
|
725
|
+
return this._setKVNode(key, value)?.node;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
if (succ && !this.isRealNode(succ.left)) {
|
|
729
|
+
const newNode = this.createNode(key, value);
|
|
730
|
+
if (!this.isRealNode(newNode)) return undefined;
|
|
731
|
+
this._attachNewNode(succ, 'left', newNode);
|
|
732
|
+
if (this._isMapMode) this._store.set(key, newNode);
|
|
733
|
+
this._size++;
|
|
734
|
+
// Maintain header/min/max caches.
|
|
735
|
+
const NIL = this.NIL;
|
|
736
|
+
const hMin = this._header._left ?? NIL;
|
|
737
|
+
if (hMin === NIL || this._compare(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
738
|
+
const hMax = this._header._right ?? NIL;
|
|
739
|
+
if (hMax === NIL || this._compare(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
740
|
+
return newNode;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
return this._setKVNode(key, value)?.node;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Boolean wrapper for setWithHintNode.
|
|
748
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
749
|
+
*/
|
|
750
|
+
setWithHint(key: K, value: V, hint?: RedBlackTreeNode<K, V>): boolean {
|
|
751
|
+
return this.setWithHintNode(key, value, hint) !== undefined;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Insert or update a key/value (map mode) or key-only (set mode).
|
|
756
|
+
*
|
|
757
|
+
* This method is optimized for:
|
|
758
|
+
* - monotonic inserts via min/max boundary fast paths
|
|
759
|
+
* - updates via a single-pass search (no double walk)
|
|
760
|
+
*
|
|
761
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
352
762
|
*/
|
|
353
763
|
override set(
|
|
354
764
|
keyNodeOrEntry: K | RedBlackTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
|
|
355
765
|
value?: V
|
|
356
766
|
): boolean {
|
|
767
|
+
// Common path: tree.set(key, value) or tree.set([key, value]).
|
|
768
|
+
if (!this.isNode(keyNodeOrEntry)) {
|
|
769
|
+
if (keyNodeOrEntry === null || keyNodeOrEntry === undefined) return false;
|
|
770
|
+
|
|
771
|
+
if (this.isEntry(keyNodeOrEntry)) {
|
|
772
|
+
const key = keyNodeOrEntry[0];
|
|
773
|
+
if (key === null || key === undefined) return false;
|
|
774
|
+
const nextValue = value ?? keyNodeOrEntry[1];
|
|
775
|
+
return this._setKV(key, nextValue);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// key-only
|
|
779
|
+
return this._setKV(keyNodeOrEntry, value);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Node insertion path (advanced usage)
|
|
357
783
|
const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
|
358
784
|
if (!this.isRealNode(newNode)) return false;
|
|
359
785
|
|
|
@@ -365,12 +791,24 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
365
791
|
} else {
|
|
366
792
|
return false;
|
|
367
793
|
}
|
|
368
|
-
if (this._isMapMode)
|
|
794
|
+
if (this._isMapMode) {
|
|
795
|
+
const n = this.getNode(newNode.key);
|
|
796
|
+
if (this.isRealNode(n)) {
|
|
797
|
+
n.value = newValue as V;
|
|
798
|
+
this._store.set(n.key, n);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
369
801
|
this._size++;
|
|
370
802
|
return true;
|
|
371
803
|
}
|
|
372
804
|
if (insertStatus === 'UPDATED') {
|
|
373
|
-
if (this._isMapMode)
|
|
805
|
+
if (this._isMapMode) {
|
|
806
|
+
const n = this.getNode(newNode.key);
|
|
807
|
+
if (this.isRealNode(n)) {
|
|
808
|
+
n.value = newValue as V;
|
|
809
|
+
this._store.set(n.key, n);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
374
812
|
return true;
|
|
375
813
|
}
|
|
376
814
|
return false;
|
|
@@ -378,25 +816,30 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
378
816
|
|
|
379
817
|
/**
|
|
380
818
|
* Delete a node by key/node/entry and rebalance as needed.
|
|
381
|
-
* @remarks Time O(log n), Space O(1)
|
|
382
|
-
* @param
|
|
819
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
820
|
+
* @param keyNodeEntryRawOrPredicate - Key, node, or [key, value] entry identifying the node to delete.
|
|
383
821
|
* @returns Array with deletion metadata (removed node, rebalancing hint if any).
|
|
384
822
|
*/
|
|
385
|
-
|
|
386
823
|
override delete(
|
|
387
|
-
|
|
824
|
+
keyNodeEntryRawOrPredicate: BTNRep<K, V, RedBlackTreeNode<K, V>> | NodePredicate<RedBlackTreeNode<K, V> | null>
|
|
388
825
|
): BinaryTreeDeleteResult<RedBlackTreeNode<K, V>>[] {
|
|
389
|
-
if (
|
|
826
|
+
if (keyNodeEntryRawOrPredicate === null) return [];
|
|
390
827
|
|
|
391
828
|
const results: BinaryTreeDeleteResult<RedBlackTreeNode<K, V>>[] = [];
|
|
392
829
|
let nodeToDelete: OptNode<RedBlackTreeNode<K, V>>;
|
|
393
|
-
if (this._isPredicate(
|
|
394
|
-
else nodeToDelete = this.isRealNode(
|
|
830
|
+
if (this._isPredicate(keyNodeEntryRawOrPredicate)) nodeToDelete = this.getNode(keyNodeEntryRawOrPredicate);
|
|
831
|
+
else nodeToDelete = this.isRealNode(keyNodeEntryRawOrPredicate) ? keyNodeEntryRawOrPredicate : this.getNode(keyNodeEntryRawOrPredicate);
|
|
395
832
|
|
|
396
833
|
if (!nodeToDelete) {
|
|
397
834
|
return results;
|
|
398
835
|
}
|
|
399
836
|
|
|
837
|
+
// Track min/max cache updates before structural modifications.
|
|
838
|
+
const willDeleteMin = nodeToDelete === this._minNode;
|
|
839
|
+
const willDeleteMax = nodeToDelete === this._maxNode;
|
|
840
|
+
const nextMin = willDeleteMin ? this._successorOf(nodeToDelete) : undefined;
|
|
841
|
+
const nextMax = willDeleteMax ? this._predecessorOf(nodeToDelete) : undefined;
|
|
842
|
+
|
|
400
843
|
let originalColor = nodeToDelete.color;
|
|
401
844
|
let replacementNode: RedBlackTreeNode<K, V> | undefined;
|
|
402
845
|
|
|
@@ -439,6 +882,22 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
439
882
|
if (this._isMapMode) this._store.delete(nodeToDelete.key);
|
|
440
883
|
this._size--;
|
|
441
884
|
|
|
885
|
+
// Update min/max caches.
|
|
886
|
+
if (this._size <= 0) {
|
|
887
|
+
this._setMinCache(undefined);
|
|
888
|
+
this._setMaxCache(undefined);
|
|
889
|
+
} else {
|
|
890
|
+
if (willDeleteMin) this._setMinCache(nextMin);
|
|
891
|
+
if (willDeleteMax) this._setMaxCache(nextMax);
|
|
892
|
+
// Fallback if successor/predecessor was unavailable.
|
|
893
|
+
if (!this._minNode || !this.isRealNode(this._minNode)) {
|
|
894
|
+
this._setMinCache(this.isRealNode(this._root) ? this.getLeftMost(n => n, this._root) : undefined);
|
|
895
|
+
}
|
|
896
|
+
if (!this._maxNode || !this.isRealNode(this._maxNode)) {
|
|
897
|
+
this._setMaxCache(this.isRealNode(this._root) ? this.getRightMost(n => n, this._root) : undefined);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
442
901
|
if (originalColor === 'BLACK') {
|
|
443
902
|
this._deleteFixup(replacementNode);
|
|
444
903
|
}
|
|
@@ -450,7 +909,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
450
909
|
|
|
451
910
|
/**
|
|
452
911
|
* Transform entries into a like-kind red-black tree with possibly different key/value types.
|
|
453
|
-
* @remarks Time O(n), Space O(n)
|
|
912
|
+
* @remarks Time O(n) average, Space O(n)
|
|
454
913
|
* @template MK
|
|
455
914
|
* @template MV
|
|
456
915
|
* @template MR
|
|
@@ -459,7 +918,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
459
918
|
* @param [thisArg] - See parameter type for details.
|
|
460
919
|
* @returns A new RedBlackTree with mapped entries.
|
|
461
920
|
*/
|
|
462
|
-
|
|
463
921
|
override map<MK = K, MV = V, MR = any>(
|
|
464
922
|
callback: EntryCallback<K, V | undefined, [MK, MV]>,
|
|
465
923
|
options?: Partial<RedBlackTreeOptions<MK, MV, MR>>,
|
|
@@ -474,6 +932,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
474
932
|
return out;
|
|
475
933
|
}
|
|
476
934
|
|
|
935
|
+
/**
|
|
936
|
+
* (Internal) Create an empty instance of the same concrete tree type.
|
|
937
|
+
* @remarks Time O(1) average, Space O(1)
|
|
938
|
+
*/
|
|
477
939
|
protected override _createInstance<TK = K, TV = V, TR = R>(options?: Partial<RedBlackTreeOptions<TK, TV, TR>>): this {
|
|
478
940
|
const Ctor = this.constructor as unknown as new (
|
|
479
941
|
iter?: Iterable<TK | RedBlackTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR>,
|
|
@@ -482,6 +944,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
482
944
|
return new Ctor([], { ...this._snapshotOptions<TK, TV, TR>(), ...(options ?? {}) }) as unknown as this;
|
|
483
945
|
}
|
|
484
946
|
|
|
947
|
+
/**
|
|
948
|
+
* (Internal) Create a like-kind tree (same concrete class) populated from an iterable.
|
|
949
|
+
* @remarks Time O(m log m) average (m = iterable length), Space O(m)
|
|
950
|
+
*/
|
|
485
951
|
protected override _createLike<TK = K, TV = V, TR = R>(
|
|
486
952
|
iter: Iterable<
|
|
487
953
|
TK | RedBlackTreeNode<TK, TV> | [TK | null | undefined, TV | undefined] | null | undefined | TR
|
|
@@ -495,13 +961,25 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
495
961
|
return new Ctor(iter, { ...this._snapshotOptions<TK, TV, TR>(), ...(options ?? {}) });
|
|
496
962
|
}
|
|
497
963
|
|
|
964
|
+
/**
|
|
965
|
+
* (Internal) Set the root pointer and keep header.parent in sync.
|
|
966
|
+
* @remarks Time O(1), Space O(1)
|
|
967
|
+
*/
|
|
498
968
|
protected override _setRoot(v: RedBlackTreeNode<K, V> | undefined) {
|
|
969
|
+
const NIL = this.NIL;
|
|
499
970
|
if (v) {
|
|
500
971
|
v.parent = undefined;
|
|
501
972
|
}
|
|
502
973
|
this._root = v;
|
|
974
|
+
// Keep header root pointer in sync even for internal operations (rotations/transplants)
|
|
975
|
+
// and for subclasses that may bypass _setKVNode.
|
|
976
|
+
this._header.parent = v ?? NIL;
|
|
503
977
|
}
|
|
504
978
|
|
|
979
|
+
/**
|
|
980
|
+
* (Internal) Replace a node in place while preserving its color.
|
|
981
|
+
* @remarks Time O(1) average, Space O(1)
|
|
982
|
+
*/
|
|
505
983
|
protected override _replaceNode(
|
|
506
984
|
oldNode: RedBlackTreeNode<K, V>,
|
|
507
985
|
newNode: RedBlackTreeNode<K, V>
|
|
@@ -513,22 +991,25 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
513
991
|
|
|
514
992
|
/**
|
|
515
993
|
* (Protected) Standard BST insert followed by red-black fix-up.
|
|
516
|
-
* @remarks Time O(log n), Space O(1)
|
|
994
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
517
995
|
* @param node - Node to insert.
|
|
518
996
|
* @returns Status string: 'CREATED' or 'UPDATED'.
|
|
519
997
|
*/
|
|
520
|
-
|
|
521
998
|
protected _insert(node: RedBlackTreeNode<K, V>): CRUD {
|
|
522
|
-
|
|
523
|
-
|
|
999
|
+
const NIL = this.NIL;
|
|
1000
|
+
const cmp = this._compare.bind(this);
|
|
524
1001
|
|
|
525
|
-
|
|
1002
|
+
let current = this._header.parent ?? NIL;
|
|
1003
|
+
let parent: RedBlackTreeNode<K, V> | undefined;
|
|
1004
|
+
let lastCompared = 0;
|
|
1005
|
+
|
|
1006
|
+
while (current !== NIL) {
|
|
526
1007
|
parent = current;
|
|
527
|
-
|
|
528
|
-
if (
|
|
529
|
-
current = current.left ??
|
|
530
|
-
} else if (
|
|
531
|
-
current = current.right ??
|
|
1008
|
+
lastCompared = cmp(node.key, current.key);
|
|
1009
|
+
if (lastCompared < 0) {
|
|
1010
|
+
current = current.left ?? NIL;
|
|
1011
|
+
} else if (lastCompared > 0) {
|
|
1012
|
+
current = current.right ?? NIL;
|
|
532
1013
|
} else {
|
|
533
1014
|
this._replaceNode(current, node);
|
|
534
1015
|
return 'UPDATED';
|
|
@@ -539,14 +1020,14 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
539
1020
|
|
|
540
1021
|
if (!parent) {
|
|
541
1022
|
this._setRoot(node);
|
|
542
|
-
} else if (
|
|
1023
|
+
} else if (lastCompared < 0) {
|
|
543
1024
|
parent.left = node;
|
|
544
1025
|
} else {
|
|
545
1026
|
parent.right = node;
|
|
546
1027
|
}
|
|
547
1028
|
|
|
548
|
-
node.left =
|
|
549
|
-
node.right =
|
|
1029
|
+
node.left = NIL;
|
|
1030
|
+
node.right = NIL;
|
|
550
1031
|
node.color = 'RED';
|
|
551
1032
|
|
|
552
1033
|
this._insertFixup(node);
|
|
@@ -560,7 +1041,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
560
1041
|
* @param v - Replacement subtree root (may be undefined).
|
|
561
1042
|
* @returns void
|
|
562
1043
|
*/
|
|
563
|
-
|
|
564
1044
|
protected _transplant(u: RedBlackTreeNode<K, V>, v: RedBlackTreeNode<K, V> | undefined): void {
|
|
565
1045
|
if (!u.parent) {
|
|
566
1046
|
this._setRoot(v);
|
|
@@ -577,53 +1057,68 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
577
1057
|
|
|
578
1058
|
/**
|
|
579
1059
|
* (Protected) Restore red-black properties after insertion (recolor/rotate).
|
|
580
|
-
* @remarks Time O(log n), Space O(1)
|
|
1060
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
581
1061
|
* @param z - Recently inserted node.
|
|
582
1062
|
* @returns void
|
|
583
1063
|
*/
|
|
584
|
-
|
|
585
1064
|
protected _insertFixup(z: RedBlackTreeNode<K, V> | undefined): void {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
1065
|
+
const leftRotate = this._leftRotate.bind(this);
|
|
1066
|
+
const rightRotate = this._rightRotate.bind(this);
|
|
1067
|
+
|
|
1068
|
+
while (z) {
|
|
1069
|
+
const p = z.parent;
|
|
1070
|
+
if (!p || p.color !== 'RED') break;
|
|
1071
|
+
|
|
1072
|
+
const gp = p.parent;
|
|
1073
|
+
if (!gp) break;
|
|
1074
|
+
|
|
1075
|
+
if (p === gp.left) {
|
|
1076
|
+
const y = gp.right;
|
|
589
1077
|
if (y?.color === 'RED') {
|
|
590
|
-
|
|
1078
|
+
p.color = 'BLACK';
|
|
591
1079
|
y.color = 'BLACK';
|
|
592
|
-
|
|
1080
|
+
gp.color = 'RED';
|
|
1081
|
+
z = gp;
|
|
1082
|
+
continue;
|
|
1083
|
+
}
|
|
593
1084
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
this._leftRotate(z);
|
|
599
|
-
}
|
|
1085
|
+
if (z === p.right) {
|
|
1086
|
+
z = p;
|
|
1087
|
+
leftRotate(z);
|
|
1088
|
+
}
|
|
600
1089
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
1090
|
+
const p2 = z?.parent;
|
|
1091
|
+
const gp2 = p2?.parent;
|
|
1092
|
+
if (p2 && gp2) {
|
|
1093
|
+
p2.color = 'BLACK';
|
|
1094
|
+
gp2.color = 'RED';
|
|
1095
|
+
rightRotate(gp2);
|
|
606
1096
|
}
|
|
607
1097
|
} else {
|
|
608
|
-
const y
|
|
1098
|
+
const y = gp.left;
|
|
609
1099
|
if (y?.color === 'RED') {
|
|
610
|
-
|
|
1100
|
+
p.color = 'BLACK';
|
|
611
1101
|
y.color = 'BLACK';
|
|
612
|
-
|
|
613
|
-
z =
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
z = z.parent;
|
|
617
|
-
this._rightRotate(z);
|
|
618
|
-
}
|
|
1102
|
+
gp.color = 'RED';
|
|
1103
|
+
z = gp;
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
619
1106
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
1107
|
+
if (z === p.left) {
|
|
1108
|
+
z = p;
|
|
1109
|
+
rightRotate(z);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
const p2 = z?.parent;
|
|
1113
|
+
const gp2 = p2?.parent;
|
|
1114
|
+
if (p2 && gp2) {
|
|
1115
|
+
p2.color = 'BLACK';
|
|
1116
|
+
gp2.color = 'RED';
|
|
1117
|
+
leftRotate(gp2);
|
|
625
1118
|
}
|
|
626
1119
|
}
|
|
1120
|
+
|
|
1121
|
+
break;
|
|
627
1122
|
}
|
|
628
1123
|
|
|
629
1124
|
if (this.isRealNode(this._root)) this._root.color = 'BLACK';
|
|
@@ -631,11 +1126,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
631
1126
|
|
|
632
1127
|
/**
|
|
633
1128
|
* (Protected) Restore red-black properties after deletion (recolor/rotate).
|
|
634
|
-
* @remarks Time O(log n), Space O(1)
|
|
1129
|
+
* @remarks Time O(log n) average, Space O(1)
|
|
635
1130
|
* @param node - Child that replaced the deleted node (may be undefined).
|
|
636
1131
|
* @returns void
|
|
637
1132
|
*/
|
|
638
|
-
|
|
639
1133
|
protected _deleteFixup(node: RedBlackTreeNode<K, V> | undefined): void {
|
|
640
1134
|
if (!node || node === this.root || node.color === 'BLACK') {
|
|
641
1135
|
if (node) {
|
|
@@ -705,7 +1199,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
705
1199
|
* @param x - Pivot node to rotate around.
|
|
706
1200
|
* @returns void
|
|
707
1201
|
*/
|
|
708
|
-
|
|
709
1202
|
protected _leftRotate(x: RedBlackTreeNode<K, V> | undefined): void {
|
|
710
1203
|
if (!x || !x.right) {
|
|
711
1204
|
return;
|
|
@@ -738,7 +1231,6 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
738
1231
|
* @param y - Pivot node to rotate around.
|
|
739
1232
|
* @returns void
|
|
740
1233
|
*/
|
|
741
|
-
|
|
742
1234
|
protected _rightRotate(y: RedBlackTreeNode<K, V> | undefined): void {
|
|
743
1235
|
if (!y || !y.left) {
|
|
744
1236
|
return;
|