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