binary-tree-typed 2.4.4 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -84
- package/dist/cjs/index.cjs +965 -420
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +962 -417
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +965 -421
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +962 -418
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/common/error.d.ts +23 -0
- package/dist/types/common/index.d.ts +1 -0
- package/dist/types/data-structures/base/iterable-element-base.d.ts +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +128 -51
- package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +210 -164
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +439 -78
- package/dist/types/data-structures/binary-tree/bst.d.ts +311 -28
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +217 -31
- package/dist/types/data-structures/binary-tree/segment-tree.d.ts +218 -152
- package/dist/types/data-structures/binary-tree/tree-map.d.ts +1281 -5
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +1087 -201
- package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +858 -65
- package/dist/types/data-structures/binary-tree/tree-set.d.ts +1133 -5
- package/dist/types/data-structures/graph/abstract-graph.d.ts +44 -0
- package/dist/types/data-structures/graph/directed-graph.d.ts +220 -47
- package/dist/types/data-structures/graph/map-graph.d.ts +59 -1
- package/dist/types/data-structures/graph/undirected-graph.d.ts +218 -59
- package/dist/types/data-structures/hash/hash-map.d.ts +230 -77
- package/dist/types/data-structures/heap/heap.d.ts +287 -99
- package/dist/types/data-structures/heap/max-heap.d.ts +46 -0
- package/dist/types/data-structures/heap/min-heap.d.ts +59 -0
- package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +286 -44
- package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +278 -65
- package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +415 -12
- package/dist/types/data-structures/matrix/matrix.d.ts +331 -0
- package/dist/types/data-structures/priority-queue/max-priority-queue.d.ts +57 -0
- package/dist/types/data-structures/priority-queue/min-priority-queue.d.ts +60 -0
- package/dist/types/data-structures/priority-queue/priority-queue.d.ts +60 -0
- package/dist/types/data-structures/queue/deque.d.ts +313 -66
- package/dist/types/data-structures/queue/queue.d.ts +211 -42
- package/dist/types/data-structures/stack/stack.d.ts +174 -32
- package/dist/types/data-structures/trie/trie.d.ts +213 -43
- package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -1
- package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +1 -4
- package/dist/types/types/data-structures/queue/deque.d.ts +6 -0
- package/dist/umd/binary-tree-typed.js +959 -414
- package/dist/umd/binary-tree-typed.js.map +1 -1
- package/dist/umd/binary-tree-typed.min.js +3 -3
- package/dist/umd/binary-tree-typed.min.js.map +1 -1
- package/package.json +2 -2
- package/src/common/error.ts +60 -0
- package/src/common/index.ts +2 -0
- package/src/data-structures/base/iterable-element-base.ts +2 -2
- package/src/data-structures/binary-tree/avl-tree.ts +134 -51
- package/src/data-structures/binary-tree/binary-indexed-tree.ts +303 -247
- package/src/data-structures/binary-tree/binary-tree.ts +542 -121
- package/src/data-structures/binary-tree/bst.ts +346 -37
- package/src/data-structures/binary-tree/red-black-tree.ts +309 -96
- package/src/data-structures/binary-tree/segment-tree.ts +372 -248
- package/src/data-structures/binary-tree/tree-map.ts +1292 -13
- package/src/data-structures/binary-tree/tree-multi-map.ts +1098 -215
- package/src/data-structures/binary-tree/tree-multi-set.ts +863 -69
- package/src/data-structures/binary-tree/tree-set.ts +1143 -15
- package/src/data-structures/graph/abstract-graph.ts +106 -1
- package/src/data-structures/graph/directed-graph.ts +223 -47
- package/src/data-structures/graph/map-graph.ts +59 -1
- package/src/data-structures/graph/undirected-graph.ts +299 -59
- package/src/data-structures/hash/hash-map.ts +243 -79
- package/src/data-structures/heap/heap.ts +291 -102
- package/src/data-structures/heap/max-heap.ts +48 -3
- package/src/data-structures/heap/min-heap.ts +59 -0
- package/src/data-structures/linked-list/doubly-linked-list.ts +286 -44
- package/src/data-structures/linked-list/singly-linked-list.ts +278 -65
- package/src/data-structures/linked-list/skip-linked-list.ts +689 -90
- package/src/data-structures/matrix/matrix.ts +425 -22
- package/src/data-structures/priority-queue/max-priority-queue.ts +59 -3
- package/src/data-structures/priority-queue/min-priority-queue.ts +60 -0
- package/src/data-structures/priority-queue/priority-queue.ts +60 -0
- package/src/data-structures/queue/deque.ts +343 -68
- package/src/data-structures/queue/queue.ts +211 -42
- package/src/data-structures/stack/stack.ts +174 -32
- package/src/data-structures/trie/trie.ts +215 -44
- package/src/types/data-structures/binary-tree/segment-tree.ts +1 -1
- package/src/types/data-structures/linked-list/skip-linked-list.ts +2 -1
- package/src/types/data-structures/queue/deque.ts +7 -0
- package/src/utils/utils.ts +4 -2
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
FamilyPosition, NodePredicate,
|
|
14
14
|
OptNode,
|
|
15
15
|
RBTNColor,
|
|
16
|
+
IterationType,
|
|
16
17
|
RedBlackTreeOptions
|
|
17
18
|
} from '../../types';
|
|
18
19
|
import { BST } from './bst';
|
|
@@ -97,16 +98,12 @@ export class RedBlackTreeNode<K = any, V = any> {
|
|
|
97
98
|
*
|
|
98
99
|
* @returns The height.
|
|
99
100
|
*/
|
|
101
|
+
/* istanbul ignore next -- covered by AVLTree tests (subclass uses height) */
|
|
100
102
|
get height(): number {
|
|
101
103
|
return this._height;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
|
|
105
|
-
* Sets the height of the node.
|
|
106
|
-
* @remarks Time O(1), Space O(1)
|
|
107
|
-
*
|
|
108
|
-
* @param value - The new height.
|
|
109
|
-
*/
|
|
106
|
+
/* istanbul ignore next -- covered by AVLTree tests (subclass uses height) */
|
|
110
107
|
set height(value: number) {
|
|
111
108
|
this._height = value;
|
|
112
109
|
}
|
|
@@ -141,20 +138,11 @@ export class RedBlackTreeNode<K = any, V = any> {
|
|
|
141
138
|
*
|
|
142
139
|
* @returns The subtree node count.
|
|
143
140
|
*/
|
|
141
|
+
/* istanbul ignore next -- internal field, exercised indirectly via tree operations */
|
|
144
142
|
get count(): number {
|
|
145
143
|
return this._count;
|
|
146
144
|
}
|
|
147
145
|
|
|
148
|
-
/**
|
|
149
|
-
* Sets the count of nodes in the subtree.
|
|
150
|
-
* @remarks Time O(1), Space O(1)
|
|
151
|
-
*
|
|
152
|
-
* @param value - The new count.
|
|
153
|
-
*/
|
|
154
|
-
set count(value: number) {
|
|
155
|
-
this._count = value;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
146
|
/**
|
|
159
147
|
* Gets the position of the node relative to its parent.
|
|
160
148
|
* @remarks Time O(1), Space O(1)
|
|
@@ -172,6 +160,7 @@ export class RedBlackTreeNode<K = any, V = any> {
|
|
|
172
160
|
return this.left || this.right ? 'ROOT_RIGHT' : 'RIGHT';
|
|
173
161
|
}
|
|
174
162
|
|
|
163
|
+
/* istanbul ignore next -- defensive: unreachable if tree structure is correct */
|
|
175
164
|
return 'MAL_NODE';
|
|
176
165
|
}
|
|
177
166
|
}
|
|
@@ -186,23 +175,6 @@ export class RedBlackTreeNode<K = any, V = any> {
|
|
|
186
175
|
* 2. It is BST itself. Compared with Heap which is not completely ordered, RedBlackTree is completely ordered.
|
|
187
176
|
*
|
|
188
177
|
* @example
|
|
189
|
-
* // basic Red-Black Tree with simple number keys
|
|
190
|
-
* // Create a simple Red-Black Tree with numeric keys
|
|
191
|
-
* const tree = new RedBlackTree([5, 2, 8, 1, 9]);
|
|
192
|
-
*
|
|
193
|
-
* tree.print();
|
|
194
|
-
* // _2___
|
|
195
|
-
* // / \
|
|
196
|
-
* // 1 _8_
|
|
197
|
-
* // / \
|
|
198
|
-
* // 5 9
|
|
199
|
-
*
|
|
200
|
-
* // Verify the tree maintains sorted order
|
|
201
|
-
* console.log([...tree.keys()]); // [1, 2, 5, 8, 9];
|
|
202
|
-
*
|
|
203
|
-
* // Check size
|
|
204
|
-
* console.log(tree.size); // 5;
|
|
205
|
-
* @example
|
|
206
178
|
* // Red-Black Tree with key-value pairs for lookups
|
|
207
179
|
* interface Employee {
|
|
208
180
|
* id: number;
|
|
@@ -368,6 +340,46 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
368
340
|
/**
|
|
369
341
|
* Remove all nodes and clear internal caches.
|
|
370
342
|
* @remarks Time O(n) average, Space O(1)
|
|
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
|
+
* @example
|
|
379
|
+
* // Remove all entries
|
|
380
|
+
* const rbt = new RedBlackTree<number>([1, 2, 3]);
|
|
381
|
+
* rbt.clear();
|
|
382
|
+
* console.log(rbt.isEmpty()); // true;
|
|
371
383
|
*/
|
|
372
384
|
override clear() {
|
|
373
385
|
super.clear();
|
|
@@ -603,10 +615,10 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
603
615
|
if (hMin === NIL || hMax === NIL) {
|
|
604
616
|
this._setMinCache(newNode);
|
|
605
617
|
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);
|
|
618
|
+
} else /* istanbul ignore next -- boundary fast-paths at top of _setKVNode intercept boundary inserts before normal path */ if (parent === hMax && lastCompared > 0) {
|
|
619
|
+
/* istanbul ignore next */ this._setMaxCache(newNode);
|
|
620
|
+
} else /* istanbul ignore next */ if (parent === hMin && lastCompared < 0) {
|
|
621
|
+
/* istanbul ignore next */ this._setMinCache(newNode);
|
|
610
622
|
} else {
|
|
611
623
|
if (cmp(newNode.key, hMin.key) < 0) this._setMinCache(newNode);
|
|
612
624
|
if (cmp(newNode.key, hMax.key) > 0) this._setMaxCache(newNode);
|
|
@@ -700,6 +712,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
700
712
|
return newNode;
|
|
701
713
|
}
|
|
702
714
|
|
|
715
|
+
/* istanbul ignore next -- structurally unreachable: predecessor never has a right child (it's the max of left subtree) */
|
|
703
716
|
return this._setKVNode(key, value)?.node;
|
|
704
717
|
}
|
|
705
718
|
|
|
@@ -740,6 +753,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
740
753
|
return newNode;
|
|
741
754
|
}
|
|
742
755
|
|
|
756
|
+
/* istanbul ignore next -- structurally unreachable: successor never has a left child (it's the min of right subtree) */
|
|
743
757
|
return this._setKVNode(key, value)?.node;
|
|
744
758
|
}
|
|
745
759
|
|
|
@@ -759,6 +773,59 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
759
773
|
* - updates via a single-pass search (no double walk)
|
|
760
774
|
*
|
|
761
775
|
* @remarks Time O(log n) average, Space O(1)
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
* @example
|
|
813
|
+
* // basic Red-Black Tree with simple number keys
|
|
814
|
+
* // Create a simple Red-Black Tree with numeric keys
|
|
815
|
+
* const tree = new RedBlackTree([5, 2, 8, 1, 9]);
|
|
816
|
+
*
|
|
817
|
+
* tree.print();
|
|
818
|
+
* // _2___
|
|
819
|
+
* // / \
|
|
820
|
+
* // 1 _8_
|
|
821
|
+
* // / \
|
|
822
|
+
* // 5 9
|
|
823
|
+
*
|
|
824
|
+
* // Verify the tree maintains sorted order
|
|
825
|
+
* console.log([...tree.keys()]); // [1, 2, 5, 8, 9];
|
|
826
|
+
*
|
|
827
|
+
* // Check size
|
|
828
|
+
* console.log(tree.size); // 5;
|
|
762
829
|
*/
|
|
763
830
|
override set(
|
|
764
831
|
keyNodeOrEntry: K | RedBlackTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
|
|
@@ -811,6 +878,7 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
811
878
|
}
|
|
812
879
|
return true;
|
|
813
880
|
}
|
|
881
|
+
/* istanbul ignore next -- defensive: _insert only returns CREATED|UPDATED */
|
|
814
882
|
return false;
|
|
815
883
|
}
|
|
816
884
|
|
|
@@ -819,6 +887,50 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
819
887
|
* @remarks Time O(log n) average, Space O(1)
|
|
820
888
|
* @param keyNodeEntryRawOrPredicate - Key, node, or [key, value] entry identifying the node to delete.
|
|
821
889
|
* @returns Array with deletion metadata (removed node, rebalancing hint if any).
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
* @example
|
|
929
|
+
* // Remove and rebalance
|
|
930
|
+
* const rbt = new RedBlackTree<number>([10, 5, 15, 3, 7]);
|
|
931
|
+
* rbt.delete(5);
|
|
932
|
+
* console.log(rbt.has(5)); // false;
|
|
933
|
+
* console.log(rbt.size); // 4;
|
|
822
934
|
*/
|
|
823
935
|
override delete(
|
|
824
936
|
keyNodeEntryRawOrPredicate: BTNRep<K, V, RedBlackTreeNode<K, V>> | NodePredicate<RedBlackTreeNode<K, V> | null>
|
|
@@ -841,39 +953,38 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
841
953
|
const nextMax = willDeleteMax ? this._predecessorOf(nodeToDelete) : undefined;
|
|
842
954
|
|
|
843
955
|
let originalColor = nodeToDelete.color;
|
|
844
|
-
|
|
956
|
+
const NIL = this.NIL;
|
|
957
|
+
let replacementNode: RedBlackTreeNode<K, V> = NIL;
|
|
845
958
|
|
|
846
959
|
if (!this.isRealNode(nodeToDelete.left)) {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
}
|
|
960
|
+
// No real left child → replace with right (may be NIL)
|
|
961
|
+
replacementNode = nodeToDelete.right ?? NIL;
|
|
962
|
+
this._transplant(nodeToDelete, replacementNode);
|
|
851
963
|
} else if (!this.isRealNode(nodeToDelete.right)) {
|
|
964
|
+
// No real right child → replace with left
|
|
852
965
|
replacementNode = nodeToDelete.left;
|
|
853
|
-
this._transplant(nodeToDelete,
|
|
966
|
+
this._transplant(nodeToDelete, replacementNode);
|
|
854
967
|
} else {
|
|
968
|
+
// Two children → find in-order successor
|
|
855
969
|
const successor = this.getLeftMost(node => node, nodeToDelete.right);
|
|
856
970
|
if (successor) {
|
|
857
971
|
originalColor = successor.color;
|
|
858
|
-
|
|
972
|
+
replacementNode = successor.right ?? NIL;
|
|
859
973
|
|
|
860
974
|
if (successor.parent === nodeToDelete) {
|
|
861
|
-
if
|
|
862
|
-
|
|
863
|
-
}
|
|
975
|
+
// Even if replacementNode is NIL, set its parent for fixup
|
|
976
|
+
replacementNode.parent = successor;
|
|
864
977
|
} else {
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
}
|
|
869
|
-
if (this.isRealNode(successor.right)) {
|
|
978
|
+
this._transplant(successor, replacementNode);
|
|
979
|
+
successor.right = nodeToDelete.right;
|
|
980
|
+
if (successor.right) {
|
|
870
981
|
successor.right.parent = successor;
|
|
871
982
|
}
|
|
872
983
|
}
|
|
873
984
|
|
|
874
985
|
this._transplant(nodeToDelete, successor);
|
|
875
986
|
successor.left = nodeToDelete.left;
|
|
876
|
-
if (
|
|
987
|
+
if (successor.left) {
|
|
877
988
|
successor.left.parent = successor;
|
|
878
989
|
}
|
|
879
990
|
successor.color = nodeToDelete.color;
|
|
@@ -918,6 +1029,98 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
918
1029
|
* @param [thisArg] - See parameter type for details.
|
|
919
1030
|
* @returns A new RedBlackTree with mapped entries.
|
|
920
1031
|
*/
|
|
1032
|
+
/**
|
|
1033
|
+
* Red-Black trees are self-balancing — `perfectlyBalance` rebuilds via
|
|
1034
|
+
* sorted bulk insert, which naturally produces a balanced RBT.
|
|
1035
|
+
* @remarks Time O(N), Space O(N)
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
|
|
1060
|
+
|
|
1061
|
+
* @example
|
|
1062
|
+
* // Rebalance tree
|
|
1063
|
+
* const rbt = new RedBlackTree<number>([1, 2, 3, 4, 5]);
|
|
1064
|
+
* rbt.perfectlyBalance();
|
|
1065
|
+
* console.log(rbt.isAVLBalanced()); // true;
|
|
1066
|
+
*/
|
|
1067
|
+
override perfectlyBalance(_iterationType?: IterationType): boolean {
|
|
1068
|
+
// Extract sorted entries, clear, re-insert — RBT self-balances on insert
|
|
1069
|
+
const entries: [K, V | undefined][] = [];
|
|
1070
|
+
for (const [key, value] of this) entries.push([key, value]);
|
|
1071
|
+
if (entries.length <= 1) return true;
|
|
1072
|
+
this.clear();
|
|
1073
|
+
this.setMany(
|
|
1074
|
+
entries.map(([k]) => k),
|
|
1075
|
+
entries.map(([, v]) => v),
|
|
1076
|
+
true // isBalanceAdd
|
|
1077
|
+
);
|
|
1078
|
+
return true;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* Transform to new tree
|
|
1083
|
+
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
|
|
1104
|
+
|
|
1105
|
+
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
* @example
|
|
1119
|
+
* // Transform to new tree
|
|
1120
|
+
* const rbt = new RedBlackTree<number, number>([[1, 10], [2, 20]]);
|
|
1121
|
+
* const doubled = rbt.map((v, k) => [k, (v ?? 0) * 2] as [number, number]);
|
|
1122
|
+
* console.log([...doubled.values()]); // [20, 40];
|
|
1123
|
+
*/
|
|
921
1124
|
override map<MK = K, MV = V, MR = any>(
|
|
922
1125
|
callback: EntryCallback<K, V | undefined, [MK, MV]>,
|
|
923
1126
|
options?: Partial<RedBlackTreeOptions<MK, MV, MR>>,
|
|
@@ -1131,66 +1334,76 @@ export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implem
|
|
|
1131
1334
|
* @returns void
|
|
1132
1335
|
*/
|
|
1133
1336
|
protected _deleteFixup(node: RedBlackTreeNode<K, V> | undefined): void {
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
return;
|
|
1139
|
-
}
|
|
1337
|
+
// Standard CLRS RB-DELETE-FIXUP: restore black-height invariant.
|
|
1338
|
+
// `node` is the child that replaced the deleted node (may be NIL sentinel).
|
|
1339
|
+
// If RED → just recolor BLACK (trivial fix). If BLACK → double-black repair.
|
|
1340
|
+
if (!node) return;
|
|
1140
1341
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1342
|
+
const NIL = this.NIL;
|
|
1343
|
+
let current: RedBlackTreeNode<K, V> = node;
|
|
1143
1344
|
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1345
|
+
while (current !== this.root && current.color === 'BLACK') {
|
|
1346
|
+
const parent: RedBlackTreeNode<K, V> | undefined = current.parent;
|
|
1347
|
+
if (!parent) break;
|
|
1147
1348
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1349
|
+
const nodeIsLeft = current === parent.left;
|
|
1350
|
+
let sibling = nodeIsLeft ? parent.right : parent.left;
|
|
1150
1351
|
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1352
|
+
// Case 1: sibling is RED → rotate to get a BLACK sibling
|
|
1353
|
+
if (sibling && sibling.color === 'RED') {
|
|
1354
|
+
sibling.color = 'BLACK';
|
|
1355
|
+
parent.color = 'RED';
|
|
1356
|
+
if (nodeIsLeft) {
|
|
1154
1357
|
this._leftRotate(parent);
|
|
1155
1358
|
sibling = parent.right;
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
if ((sibling?.left?.color ?? 'BLACK') === 'BLACK') {
|
|
1159
|
-
if (sibling) sibling.color = 'RED';
|
|
1160
|
-
node = parent;
|
|
1161
1359
|
} else {
|
|
1162
|
-
if (sibling?.left) sibling.left.color = 'BLACK';
|
|
1163
|
-
if (sibling) sibling.color = parent.color;
|
|
1164
|
-
parent.color = 'BLACK';
|
|
1165
1360
|
this._rightRotate(parent);
|
|
1166
|
-
|
|
1361
|
+
sibling = parent.left;
|
|
1167
1362
|
}
|
|
1168
|
-
}
|
|
1169
|
-
let sibling = parent.left;
|
|
1363
|
+
}
|
|
1170
1364
|
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
if (parent) sibling = parent.left;
|
|
1176
|
-
}
|
|
1365
|
+
const sibLeft = sibling?.left;
|
|
1366
|
+
const sibRight = sibling?.right;
|
|
1367
|
+
const sibLeftBlack = !sibLeft || sibLeft === NIL || sibLeft.color === 'BLACK';
|
|
1368
|
+
const sibRightBlack = !sibRight || sibRight === NIL || sibRight.color === 'BLACK';
|
|
1177
1369
|
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1370
|
+
if (sibLeftBlack && sibRightBlack) {
|
|
1371
|
+
// Case 2: sibling's children are both BLACK → recolor sibling RED, move up
|
|
1372
|
+
if (sibling) sibling.color = 'RED';
|
|
1373
|
+
current = parent;
|
|
1374
|
+
} else {
|
|
1375
|
+
if (nodeIsLeft) {
|
|
1376
|
+
// Case 3: sibling's right child is BLACK → rotate sibling right first
|
|
1377
|
+
if (sibRightBlack) {
|
|
1378
|
+
if (sibLeft) sibLeft.color = 'BLACK';
|
|
1379
|
+
if (sibling) sibling.color = 'RED';
|
|
1380
|
+
if (sibling) this._rightRotate(sibling);
|
|
1381
|
+
sibling = parent.right;
|
|
1382
|
+
}
|
|
1383
|
+
// Case 4: sibling's right child is RED → final rotation
|
|
1183
1384
|
if (sibling) sibling.color = parent.color;
|
|
1184
|
-
|
|
1385
|
+
parent.color = 'BLACK';
|
|
1386
|
+
if (sibling?.right) sibling.right.color = 'BLACK';
|
|
1185
1387
|
this._leftRotate(parent);
|
|
1186
|
-
|
|
1388
|
+
} else {
|
|
1389
|
+
// Case 3 (mirror): sibling's left child is BLACK → rotate sibling left first
|
|
1390
|
+
if (sibLeftBlack) {
|
|
1391
|
+
if (sibRight) sibRight.color = 'BLACK';
|
|
1392
|
+
if (sibling) sibling.color = 'RED';
|
|
1393
|
+
if (sibling) this._leftRotate(sibling);
|
|
1394
|
+
sibling = parent.left;
|
|
1395
|
+
}
|
|
1396
|
+
// Case 4 (mirror): sibling's left child is RED → final rotation
|
|
1397
|
+
if (sibling) sibling.color = parent.color;
|
|
1398
|
+
parent.color = 'BLACK';
|
|
1399
|
+
if (sibling?.left) sibling.left.color = 'BLACK';
|
|
1400
|
+
this._rightRotate(parent);
|
|
1187
1401
|
}
|
|
1402
|
+
current = this.root!;
|
|
1188
1403
|
}
|
|
1189
1404
|
}
|
|
1190
1405
|
|
|
1191
|
-
|
|
1192
|
-
node.color = 'BLACK';
|
|
1193
|
-
}
|
|
1406
|
+
current.color = 'BLACK';
|
|
1194
1407
|
}
|
|
1195
1408
|
|
|
1196
1409
|
/**
|