data-structure-typed 2.2.7 → 2.3.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/.github/workflows/ci.yml +9 -0
- package/CHANGELOG.md +1 -1
- package/README.md +14 -3
- package/README_CN.md +119 -275
- package/benchmark/report.html +1 -1
- package/benchmark/report.json +20 -324
- package/dist/cjs/index.cjs +689 -182
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +693 -185
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +689 -182
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +693 -185
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/leetcode/avl-tree-counter.mjs +2957 -0
- package/dist/leetcode/avl-tree-multi-map.mjs +2889 -0
- package/dist/leetcode/avl-tree.mjs +2720 -0
- package/dist/leetcode/binary-tree.mjs +1594 -0
- package/dist/leetcode/bst.mjs +2398 -0
- package/dist/leetcode/deque.mjs +683 -0
- package/dist/leetcode/directed-graph.mjs +1733 -0
- package/dist/leetcode/doubly-linked-list.mjs +709 -0
- package/dist/leetcode/hash-map.mjs +493 -0
- package/dist/leetcode/heap.mjs +542 -0
- package/dist/leetcode/max-heap.mjs +375 -0
- package/dist/leetcode/max-priority-queue.mjs +383 -0
- package/dist/leetcode/min-heap.mjs +363 -0
- package/dist/leetcode/min-priority-queue.mjs +371 -0
- package/dist/leetcode/priority-queue.mjs +363 -0
- package/dist/leetcode/queue.mjs +943 -0
- package/dist/leetcode/red-black-tree.mjs +2765 -0
- package/dist/leetcode/singly-linked-list.mjs +754 -0
- package/dist/leetcode/stack.mjs +217 -0
- package/dist/leetcode/tree-counter.mjs +3039 -0
- package/dist/leetcode/tree-multi-map.mjs +2913 -0
- package/dist/leetcode/trie.mjs +413 -0
- package/dist/leetcode/undirected-graph.mjs +1650 -0
- package/dist/types/data-structures/base/linear-base.d.ts +6 -6
- package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +2 -2
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +10 -10
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +25 -27
- package/dist/types/data-structures/binary-tree/bst.d.ts +13 -12
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +151 -21
- package/dist/types/data-structures/binary-tree/tree-counter.d.ts +4 -4
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +2 -2
- package/dist/types/interfaces/binary-tree.d.ts +1 -1
- package/dist/umd/data-structure-typed.js +689 -181
- package/dist/umd/data-structure-typed.js.map +1 -1
- package/dist/umd/data-structure-typed.min.js +3 -3
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +50 -172
- package/src/data-structures/base/linear-base.ts +2 -12
- package/src/data-structures/binary-tree/avl-tree-counter.ts +6 -6
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +13 -13
- package/src/data-structures/binary-tree/avl-tree.ts +15 -15
- package/src/data-structures/binary-tree/binary-tree.ts +57 -60
- package/src/data-structures/binary-tree/bst.ts +100 -26
- package/src/data-structures/binary-tree/red-black-tree.ts +586 -76
- package/src/data-structures/binary-tree/tree-counter.ts +25 -13
- package/src/data-structures/binary-tree/tree-multi-map.ts +13 -13
- package/src/data-structures/queue/deque.ts +10 -0
- package/src/interfaces/binary-tree.ts +1 -1
- package/test/performance/data-structures/binary-tree/red-black-tree.test.ts +1 -2
- package/test/unit/data-structures/base/iterable-element-base.coverage.test.ts +106 -0
- package/test/unit/data-structures/base/iterable-element-base.more-branches.coverage.test.ts +61 -0
- package/test/unit/data-structures/base/linear-base.array.coverage.test.ts +168 -0
- package/test/unit/data-structures/base/linear-base.concat-else.coverage.test.ts +82 -0
- package/test/unit/data-structures/base/linear-base.coverage.test.ts +72 -0
- package/test/unit/data-structures/base/linear-base.more-branches.coverage.test.ts +417 -0
- package/test/unit/data-structures/binary-tree/avl-tree-counter.more-branches-3.coverage.test.ts +146 -0
- package/test/unit/data-structures/binary-tree/avl-tree-counter.more-branches.coverage.test.ts +93 -0
- package/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +30 -30
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.coverage.test.ts +108 -0
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.more-branches-2.coverage.test.ts +85 -0
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts +46 -46
- package/test/unit/data-structures/binary-tree/avl-tree-node.familyPosition-root-left.coverage.test.ts +17 -0
- package/test/unit/data-structures/binary-tree/avl-tree.more-branches-2.coverage.test.ts +99 -0
- package/test/unit/data-structures/binary-tree/avl-tree.test.ts +43 -43
- package/test/unit/data-structures/binary-tree/binary-indexed-tree.more-branches.coverage.test.ts +18 -0
- package/test/unit/data-structures/binary-tree/binary-tree.more-branches.coverage.test.ts +56 -0
- package/test/unit/data-structures/binary-tree/binary-tree.remaining-branches.coverage.test.ts +229 -0
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +151 -151
- package/test/unit/data-structures/binary-tree/bst.bound-by-predicate.coverage.test.ts +33 -0
- package/test/unit/data-structures/binary-tree/bst.coverage.test.ts +94 -0
- package/test/unit/data-structures/binary-tree/bst.deletebykey.coverage.test.ts +70 -0
- package/test/unit/data-structures/binary-tree/bst.deletewhere.coverage.test.ts +37 -0
- package/test/unit/data-structures/binary-tree/bst.floor-lower-predicate.coverage.test.ts +29 -0
- package/test/unit/data-structures/binary-tree/bst.floor-setmany.coverage.test.ts +72 -0
- package/test/unit/data-structures/binary-tree/bst.getnode.range-ensure.coverage.test.ts +22 -0
- package/test/unit/data-structures/binary-tree/bst.misc-branches.coverage.test.ts +100 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-2.coverage.test.ts +133 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-3.coverage.test.ts +45 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-4.coverage.test.ts +36 -0
- package/test/unit/data-structures/binary-tree/bst.more-branches-5.coverage.test.ts +40 -0
- package/test/unit/data-structures/binary-tree/bst.more.coverage.test.ts +39 -0
- package/test/unit/data-structures/binary-tree/bst.node-family.coverage.test.ts +29 -0
- package/test/unit/data-structures/binary-tree/bst.range-pruning.coverage.test.ts +43 -0
- package/test/unit/data-structures/binary-tree/bst.search-fastpath.coverage.test.ts +30 -0
- package/test/unit/data-structures/binary-tree/bst.test.ts +124 -154
- package/test/unit/data-structures/binary-tree/overall.test.ts +20 -20
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-corruption-repair.coverage.test.ts +66 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-max-update.coverage.test.ts +18 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-null.coverage.test.ts +53 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-stale-cache.coverage.test.ts +25 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.boundary-update.coverage.test.ts +23 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.cache-delete.coverage.test.ts +49 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.cache-edge.coverage.test.ts +37 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.cache-stale-insert.coverage.test.ts +39 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.coverage.test.ts +334 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.delete-fixup.coverage.test.ts +68 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.delete-successor.coverage.test.ts +75 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.factories.coverage.test.ts +26 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-compare-update.coverage.test.ts +74 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-no-update.coverage.test.ts +44 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-nullish.coverage.test.ts +61 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-mapmode-defined.coverage.test.ts +35 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-mapmode-undefined.coverage.test.ts +43 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint-more.coverage.test.ts +99 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.hint.coverage.test.ts +60 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.insert-cache-nullish.coverage.test.ts +29 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.insert-header-parent-nullish.coverage.test.ts +17 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.internal-walk.coverage.test.ts +57 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.minmax-cache.test.ts +65 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.misc-inputs.coverage.test.ts +17 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-2.coverage.test.ts +121 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-3.coverage.test.ts +55 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.more-branches-4.coverage.test.ts +44 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.predsucc.coverage.test.ts +40 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.remaining-branches.coverage.test.ts +123 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.set-inputs.coverage.test.ts +64 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-parent-cache.coverage.test.ts +79 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-remaining.coverage.test.ts +44 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.setkvnode-uncovered.coverage.test.ts +74 -0
- package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +141 -141
- package/test/unit/data-structures/binary-tree/red-black-tree.update-branches.coverage.test.ts +30 -0
- package/test/unit/data-structures/binary-tree/segment-tree.more-branches.coverage.test.ts +31 -0
- package/test/unit/data-structures/binary-tree/tree-counter.coverage.test.ts +115 -0
- package/test/unit/data-structures/binary-tree/tree-counter.more-branches.coverage.test.ts +244 -0
- package/test/unit/data-structures/binary-tree/tree-counter.test.ts +41 -39
- package/test/unit/data-structures/binary-tree/tree-multi-map.coverage.test.ts +104 -0
- package/test/unit/data-structures/binary-tree/tree-multi-map.more-branches-2.coverage.test.ts +59 -0
- package/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +145 -145
- package/test/unit/data-structures/graph/abstract-graph.more-branches-2.coverage.test.ts +40 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches-3.coverage.test.ts +65 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches-4.coverage.test.ts +98 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches-5.coverage.test.ts +51 -0
- package/test/unit/data-structures/graph/abstract-graph.more-branches.coverage.test.ts +62 -0
- package/test/unit/data-structures/graph/directed-graph.more-branches-2.coverage.test.ts +38 -0
- package/test/unit/data-structures/graph/directed-graph.more-branches-3.coverage.test.ts +25 -0
- package/test/unit/data-structures/graph/directed-graph.more-branches.coverage.test.ts +82 -0
- package/test/unit/data-structures/graph/map-graph.more-branches.coverage.test.ts +22 -0
- package/test/unit/data-structures/graph/undirected-graph.more-branches-2.coverage.test.ts +35 -0
- package/test/unit/data-structures/graph/undirected-graph.more-branches.coverage.test.ts +87 -0
- package/test/unit/data-structures/hash/hash-map.more-branches.coverage.test.ts +64 -0
- package/test/unit/data-structures/hash/hash-map.toEntryFn-branch.coverage.test.ts +9 -0
- package/test/unit/data-structures/heap/heap.misc-branches.coverage.test.ts +110 -0
- package/test/unit/data-structures/heap/heap.remaining-branches.coverage.test.ts +22 -0
- package/test/unit/data-structures/heap/max-heap.coverage.test.ts +29 -0
- package/test/unit/data-structures/linked-list/doubly-linked-list.more-branches.coverage.test.ts +72 -0
- package/test/unit/data-structures/linked-list/linked-list.unshiftMany-else.coverage.test.ts +15 -0
- package/test/unit/data-structures/linked-list/singly-linked-list.coverage.test.ts +221 -0
- package/test/unit/data-structures/linked-list/singly-linked-list.more-branches.coverage.test.ts +86 -0
- package/test/unit/data-structures/linked-list/skip-linked-list.more-branches.coverage.test.ts +31 -0
- package/test/unit/data-structures/matrix/matrix.more-branches.coverage.test.ts +81 -0
- package/test/unit/data-structures/matrix/matrix.pivotElement-nullish.coverage.test.ts +28 -0
- package/test/unit/data-structures/priority-queue/max-priority-queue.more-branches.coverage.test.ts +10 -0
- package/test/unit/data-structures/priority-queue/priority-queue.coverage.test.ts +21 -0
- package/test/unit/data-structures/queue/deque.coverage.test.ts +173 -0
- package/test/unit/data-structures/queue/deque.more-branches-2.coverage.test.ts +39 -0
- package/test/unit/data-structures/queue/deque.more-branches-3.coverage.test.ts +9 -0
- package/test/unit/data-structures/queue/deque.more-branches.coverage.test.ts +95 -0
- package/test/unit/data-structures/queue/queue.coverage.test.ts +138 -0
- package/test/unit/data-structures/queue/queue.more-branches-2.coverage.test.ts +27 -0
- package/test/unit/data-structures/stack/stack.coverage.test.ts +112 -0
- package/test/unit/data-structures/tree/tree.more-branches.coverage.test.ts +9 -0
- package/test/unit/data-structures/trie/trie.more-branches-2.coverage.test.ts +51 -0
- package/test/utils/patch.ts +33 -0
- package/tsup.config.js +50 -21
- package/tsup.umd.config.js +29 -0
- package/tsup.node.config.js +0 -83
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { RedBlackTree, RedBlackTreeNode } from '../../../../src';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function validateRedBlackTree<K, V>(tree: RedBlackTree<K, V>) {
|
|
5
|
+
const NIL = tree.NIL as unknown as RedBlackTreeNode<K, V>;
|
|
6
|
+
const root = tree.root as unknown as RedBlackTreeNode<K, V> | undefined;
|
|
7
|
+
|
|
8
|
+
// Empty tree is valid.
|
|
9
|
+
if (!root || root === NIL) return;
|
|
10
|
+
|
|
11
|
+
// Root must be black.
|
|
12
|
+
expect(root.color).toBe('BLACK');
|
|
13
|
+
|
|
14
|
+
const seen = new Set<RedBlackTreeNode<K, V>>();
|
|
15
|
+
|
|
16
|
+
const check = (
|
|
17
|
+
node: RedBlackTreeNode<K, V>,
|
|
18
|
+
parent: RedBlackTreeNode<K, V> | undefined,
|
|
19
|
+
min: any,
|
|
20
|
+
max: any
|
|
21
|
+
) => {
|
|
22
|
+
if (node === NIL) return;
|
|
23
|
+
|
|
24
|
+
// Basic structural sanity
|
|
25
|
+
expect(node).toBeInstanceOf(RedBlackTreeNode);
|
|
26
|
+
// Some implementations keep root.parent as undefined; tolerate that.
|
|
27
|
+
if (parent !== undefined) {
|
|
28
|
+
expect(node.parent === parent || node.parent === undefined).toBe(true);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// No cycles
|
|
32
|
+
expect(seen.has(node)).toBe(false);
|
|
33
|
+
seen.add(node);
|
|
34
|
+
|
|
35
|
+
// BST ordering (strict)
|
|
36
|
+
if (min !== undefined) expect((node.key as any) > min).toBe(true);
|
|
37
|
+
if (max !== undefined) expect((node.key as any) < max).toBe(true);
|
|
38
|
+
|
|
39
|
+
const left = (node.left ?? NIL) as RedBlackTreeNode<K, V>;
|
|
40
|
+
const right = (node.right ?? NIL) as RedBlackTreeNode<K, V>;
|
|
41
|
+
|
|
42
|
+
// Red property
|
|
43
|
+
if (node.color === 'RED') {
|
|
44
|
+
expect(left.color).toBe('BLACK');
|
|
45
|
+
expect(right.color).toBe('BLACK');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
check(left, node, min, node.key as any);
|
|
49
|
+
check(right, node, node.key as any, max);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
check(root, root.parent, undefined, undefined);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
describe('RedBlackTree coverage push (keep @example tests intact)', () => {
|
|
56
|
+
it('RedBlackTreeNode.familyPosition covers ROOT/LEFT/RIGHT/ISOLATED cases', () => {
|
|
57
|
+
const isolated = new RedBlackTreeNode<number, number>(1);
|
|
58
|
+
expect(isolated.familyPosition).toBe('ISOLATED');
|
|
59
|
+
|
|
60
|
+
// Root without parent but with a child
|
|
61
|
+
const root = new RedBlackTreeNode<number, number>(10);
|
|
62
|
+
const left = new RedBlackTreeNode<number, number>(5);
|
|
63
|
+
root.left = left;
|
|
64
|
+
left.parent = root;
|
|
65
|
+
expect(root.familyPosition).toBe('ROOT');
|
|
66
|
+
|
|
67
|
+
// Left child leaf
|
|
68
|
+
expect(left.familyPosition).toBe('LEFT');
|
|
69
|
+
|
|
70
|
+
// Left child that itself has a child -> ROOT_LEFT
|
|
71
|
+
left.left = new RedBlackTreeNode<number, number>(2);
|
|
72
|
+
left.left.parent = left;
|
|
73
|
+
expect(left.familyPosition).toBe('ROOT_LEFT');
|
|
74
|
+
|
|
75
|
+
// Right child leaf
|
|
76
|
+
const right = new RedBlackTreeNode<number, number>(15);
|
|
77
|
+
root.right = right;
|
|
78
|
+
right.parent = root;
|
|
79
|
+
expect(right.familyPosition).toBe('RIGHT');
|
|
80
|
+
|
|
81
|
+
// Right child with its own child -> ROOT_RIGHT
|
|
82
|
+
right.right = new RedBlackTreeNode<number, number>(20);
|
|
83
|
+
right.right.parent = right;
|
|
84
|
+
expect(right.familyPosition).toBe('ROOT_RIGHT');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('clear() resets root/header caches and keeps tree usable', () => {
|
|
88
|
+
const t = new RedBlackTree<number, number>();
|
|
89
|
+
t.set(2, 2);
|
|
90
|
+
t.set(1, 1);
|
|
91
|
+
t.set(3, 3);
|
|
92
|
+
|
|
93
|
+
expect(t.size).toBe(3);
|
|
94
|
+
expect(t.get(2)).toBe(2);
|
|
95
|
+
|
|
96
|
+
t.clear();
|
|
97
|
+
|
|
98
|
+
expect(t.size).toBe(0);
|
|
99
|
+
// Internal root is reset to NIL.
|
|
100
|
+
expect(t.root).toBe(t.NIL);
|
|
101
|
+
expect(t.get(2)).toBe(undefined);
|
|
102
|
+
|
|
103
|
+
// Can insert again after clear
|
|
104
|
+
t.set(4, 4);
|
|
105
|
+
expect(t.size).toBe(1);
|
|
106
|
+
expect(t.get(4)).toBe(4);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('setWithHintNode covers fast-attach + fallback branches (map mode)', () => {
|
|
110
|
+
// Default is map-mode.
|
|
111
|
+
const t = new RedBlackTree<number, string>();
|
|
112
|
+
|
|
113
|
+
// Seed tree
|
|
114
|
+
t.set(10, 'a');
|
|
115
|
+
t.set(20, 'b');
|
|
116
|
+
t.set(5, 'c');
|
|
117
|
+
|
|
118
|
+
const hint10 = t.getNode(10)!;
|
|
119
|
+
|
|
120
|
+
// c0 === 0 update branch
|
|
121
|
+
const same = t.setWithHintNode(10, 'a2', hint10);
|
|
122
|
+
expect(same).toBe(hint10);
|
|
123
|
+
expect(t.get(10)).toBe('a2');
|
|
124
|
+
|
|
125
|
+
// c0 < 0 and direct attach to hint.left (empty)
|
|
126
|
+
const n7 = t.setWithHintNode(7, 'v7', hint10);
|
|
127
|
+
expect(n7?.key).toBe(7);
|
|
128
|
+
expect(t.get(7)).toBe('v7');
|
|
129
|
+
|
|
130
|
+
// c0 > 0 direct attach to hint.right (empty)
|
|
131
|
+
const n15 = t.setWithHintNode(15, 'v15', hint10);
|
|
132
|
+
expect(n15?.key).toBe(15);
|
|
133
|
+
expect(t.get(15)).toBe('v15');
|
|
134
|
+
|
|
135
|
+
// Force predecessor/successor branches:
|
|
136
|
+
// Insert near left side using a "bad" hint so it needs pred logic.
|
|
137
|
+
const hint20 = t.getNode(20)!;
|
|
138
|
+
const n17 = t.setWithHintNode(17, 'v17', hint20);
|
|
139
|
+
expect(n17?.key).toBe(17);
|
|
140
|
+
expect(t.get(17)).toBe('v17');
|
|
141
|
+
|
|
142
|
+
// Insert near right side using a "bad" hint so it needs succ logic.
|
|
143
|
+
const hint5 = t.getNode(5)!;
|
|
144
|
+
const n6 = t.setWithHintNode(6, 'v6', hint5);
|
|
145
|
+
expect(n6?.key).toBe(6);
|
|
146
|
+
expect(t.get(6)).toBe('v6');
|
|
147
|
+
|
|
148
|
+
// value === undefined in map mode should take the _setValue path, not store.set fast path
|
|
149
|
+
t.setWithHintNode(99, undefined as any, hint20);
|
|
150
|
+
expect(t.has(99)).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('setWithHintNode covers hint invalid + pred/succ early-fallback branches + set-mode update', () => {
|
|
154
|
+
// hint invalid => fallback
|
|
155
|
+
const t = new RedBlackTree<number, string>();
|
|
156
|
+
t.set(10, 'a');
|
|
157
|
+
t.set(20, 'b');
|
|
158
|
+
t.set(5, 'c');
|
|
159
|
+
|
|
160
|
+
const other = new RedBlackTree<number, string>();
|
|
161
|
+
other.set(10, 'x');
|
|
162
|
+
const foreignHint = other.getNode(10)!;
|
|
163
|
+
|
|
164
|
+
// foreign hint should be treated as invalid for this tree; must still insert correctly.
|
|
165
|
+
const n1 = t.setWithHintNode(1, 'v1', foreignHint as any);
|
|
166
|
+
expect(n1?.key).toBe(1);
|
|
167
|
+
expect(t.get(1)).toBe('v1');
|
|
168
|
+
|
|
169
|
+
// pred early-fallback: choose hint=10, but key far left so predecessor key >= key
|
|
170
|
+
const hint10 = t.getNode(10)!;
|
|
171
|
+
const n0 = t.setWithHintNode(0, 'v0', hint10);
|
|
172
|
+
expect(n0?.key).toBe(0);
|
|
173
|
+
expect(t.get(0)).toBe('v0');
|
|
174
|
+
|
|
175
|
+
// succ early-fallback: choose hint=10, but key far right so successor key <= key
|
|
176
|
+
const n30 = t.setWithHintNode(30, 'v30', hint10);
|
|
177
|
+
expect(n30?.key).toBe(30);
|
|
178
|
+
expect(t.get(30)).toBe('v30');
|
|
179
|
+
|
|
180
|
+
// set-mode update branch (c0===0 but not map-mode)
|
|
181
|
+
const s = new RedBlackTree<number, string>([], { isMapMode: false });
|
|
182
|
+
s.set(10, 'a');
|
|
183
|
+
const h = s.getNode(10)!;
|
|
184
|
+
expect(h.value).toBe('a');
|
|
185
|
+
const same = s.setWithHintNode(10, 'b', h);
|
|
186
|
+
expect(same).toBe(h);
|
|
187
|
+
expect(s.getNode(10)?.value).toBe('b');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('_setKV map-mode fast-path updates store without changing size', () => {
|
|
191
|
+
const t = new RedBlackTree<number, string>();
|
|
192
|
+
t.set(1, 'a');
|
|
193
|
+
const n1 = t.getNode(1);
|
|
194
|
+
expect(t.size).toBe(1);
|
|
195
|
+
|
|
196
|
+
// nextValue !== undefined and store.has(key) => fast-path
|
|
197
|
+
t.set(1, 'b');
|
|
198
|
+
expect(t.size).toBe(1);
|
|
199
|
+
expect(t.get(1)).toBe('b');
|
|
200
|
+
expect(t.getNode(1)).toBe(n1);
|
|
201
|
+
|
|
202
|
+
// undefined value should still be allowed, but will not take store fast-path
|
|
203
|
+
t.set(1, undefined as any);
|
|
204
|
+
expect(t.size).toBe(1);
|
|
205
|
+
expect(t.has(1)).toBe(true);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('internal helpers: _findNodeByKey/_predecessorOf/_successorOf', () => {
|
|
209
|
+
const t = new RedBlackTree<number, number>();
|
|
210
|
+
|
|
211
|
+
// Empty tree: find returns undefined
|
|
212
|
+
expect((t as any)._findNodeByKey(1)).toBe(undefined);
|
|
213
|
+
|
|
214
|
+
// Build small tree
|
|
215
|
+
for (const k of [10, 5, 15, 3, 7, 12, 18]) t.set(k, k);
|
|
216
|
+
|
|
217
|
+
const n10 = t.getNode(10)!;
|
|
218
|
+
const n3 = t.getNode(3)!;
|
|
219
|
+
const n18 = t.getNode(18)!;
|
|
220
|
+
|
|
221
|
+
// _findNodeByKey hits <, >, and === branches
|
|
222
|
+
expect((t as any)._findNodeByKey(7)?.key).toBe(7);
|
|
223
|
+
expect((t as any)._findNodeByKey(999)).toBe(undefined);
|
|
224
|
+
|
|
225
|
+
// predecessor: node with left subtree
|
|
226
|
+
expect((t as any)._predecessorOf(n10)?.key).toBe(7);
|
|
227
|
+
// predecessor: minimum has none
|
|
228
|
+
expect((t as any)._predecessorOf(n3)).toBe(undefined);
|
|
229
|
+
|
|
230
|
+
// successor: node with right subtree
|
|
231
|
+
expect((t as any)._successorOf(n10)?.key).toBe(12);
|
|
232
|
+
// successor: maximum has none
|
|
233
|
+
expect((t as any)._successorOf(n18)).toBe(undefined);
|
|
234
|
+
});
|
|
235
|
+
const expectContentsMatch = (tree: RedBlackTree<number, number>, present: Set<number>) => {
|
|
236
|
+
for (let k = 0; k < 500; k++) {
|
|
237
|
+
expect(tree.has(k)).toBe(present.has(k));
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
it('random mixed insert/delete sequences keep invariants (exercise fixups/rotations)', () => {
|
|
242
|
+
// Deterministic pseudo-random generator (LCG) for reproducibility
|
|
243
|
+
let seed = 123456789;
|
|
244
|
+
const rand = () => {
|
|
245
|
+
seed = (1103515245 * seed + 12345) % 0x80000000;
|
|
246
|
+
return seed;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
for (let round = 0; round < 20; round++) {
|
|
250
|
+
const tree = new RedBlackTree<number, number>();
|
|
251
|
+
const present = new Set<number>();
|
|
252
|
+
|
|
253
|
+
// Insert some keys
|
|
254
|
+
for (let i = 0; i < 200; i++) {
|
|
255
|
+
const k = rand() % 500;
|
|
256
|
+
tree.set(k, k);
|
|
257
|
+
present.add(k);
|
|
258
|
+
}
|
|
259
|
+
validateRedBlackTree(tree);
|
|
260
|
+
|
|
261
|
+
// Random deletes + reinserts
|
|
262
|
+
for (let i = 0; i < 400; i++) {
|
|
263
|
+
const op = rand() % 3;
|
|
264
|
+
const k = rand() % 500;
|
|
265
|
+
if (op === 0) {
|
|
266
|
+
tree.set(k, k);
|
|
267
|
+
present.add(k);
|
|
268
|
+
} else {
|
|
269
|
+
tree.delete(k);
|
|
270
|
+
present.delete(k);
|
|
271
|
+
}
|
|
272
|
+
if (i % 25 === 0) validateRedBlackTree(tree);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Spot-check final contents agree with Set
|
|
276
|
+
expectContentsMatch(tree, present);
|
|
277
|
+
validateRedBlackTree(tree);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('targeted delete sequences (both sides) keep invariants and contents', () => {
|
|
282
|
+
const run = (keys: number[], deletes: number[]) => {
|
|
283
|
+
const t = new RedBlackTree<number, number>();
|
|
284
|
+
const present = new Set<number>();
|
|
285
|
+
for (const k of keys) {
|
|
286
|
+
t.set(k, k);
|
|
287
|
+
present.add(k);
|
|
288
|
+
}
|
|
289
|
+
validateRedBlackTree(t);
|
|
290
|
+
expectContentsMatch(t, present);
|
|
291
|
+
|
|
292
|
+
for (const d of deletes) {
|
|
293
|
+
t.delete(d);
|
|
294
|
+
present.delete(d);
|
|
295
|
+
validateRedBlackTree(t);
|
|
296
|
+
expectContentsMatch(t, present);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Iteration yields sorted keys
|
|
300
|
+
const remaining = [...t].map(([k]) => k);
|
|
301
|
+
const sorted = [...remaining].sort((a, b) => a - b);
|
|
302
|
+
expect(remaining).toEqual(sorted);
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// A fairly dense keyset; deletes include leaves, internal nodes, and root-ish nodes.
|
|
306
|
+
const keysA = [20, 10, 30, 5, 15, 25, 40, 1, 7, 12, 17, 22, 27, 35, 50, 6, 8, 11, 13, 16, 18];
|
|
307
|
+
run(keysA, [1, 7, 8, 10, 12, 15, 16, 17, 20, 22, 25]);
|
|
308
|
+
|
|
309
|
+
// Mirror-ish construction (different insertion order), tends to hit opposite-side fixups.
|
|
310
|
+
const keysB = [30, 20, 40, 10, 25, 35, 50, 5, 15, 22, 27, 33, 37, 45, 55, 1, 7, 12, 17];
|
|
311
|
+
run(keysB, [55, 50, 45, 40, 37, 35, 33, 30, 27, 25, 22]);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('deletes exercise mirror fixup sides (left/right) with deterministic shape', () => {
|
|
315
|
+
// Build a tree with enough structure to trigger both sides of _deleteFixup
|
|
316
|
+
const t = new RedBlackTree<number, number>();
|
|
317
|
+
const keys = [11, 2, 14, 1, 7, 15, 5, 8, 4, 6, 3, 13, 12, 10, 9];
|
|
318
|
+
for (const k of keys) t.set(k, k);
|
|
319
|
+
|
|
320
|
+
validateRedBlackTree(t);
|
|
321
|
+
|
|
322
|
+
// Delete sequence designed to hit different sibling/child color cases.
|
|
323
|
+
// (The exact rotations may vary, but this tends to traverse many fixup branches.)
|
|
324
|
+
for (const k of [1, 14, 2, 15, 7, 11, 8, 6, 5]) {
|
|
325
|
+
t.delete(k);
|
|
326
|
+
validateRedBlackTree(t);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Remaining keys should still be searchable and ordered
|
|
330
|
+
const remaining = [...t].map(([k]) => k);
|
|
331
|
+
const sorted = [...remaining].sort((a, b) => a - b);
|
|
332
|
+
expect(remaining).toEqual(sorted);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { RedBlackTree, RedBlackTreeNode } from '../../../../src';
|
|
2
|
+
|
|
3
|
+
function validateRedBlackTree<K, V>(tree: RedBlackTree<K, V>) {
|
|
4
|
+
const NIL = tree.NIL as unknown as RedBlackTreeNode<K, V>;
|
|
5
|
+
const root = tree.root as unknown as RedBlackTreeNode<K, V> | undefined;
|
|
6
|
+
if (!root || root === NIL) return;
|
|
7
|
+
expect(root.color).toBe('BLACK');
|
|
8
|
+
|
|
9
|
+
const seen = new Set<RedBlackTreeNode<K, V>>();
|
|
10
|
+
|
|
11
|
+
const check = (node: RedBlackTreeNode<K, V>, min: any, max: any) => {
|
|
12
|
+
if (node === NIL) return;
|
|
13
|
+
expect(seen.has(node)).toBe(false);
|
|
14
|
+
seen.add(node);
|
|
15
|
+
|
|
16
|
+
if (min !== undefined) expect((node.key as any) > min).toBe(true);
|
|
17
|
+
if (max !== undefined) expect((node.key as any) < max).toBe(true);
|
|
18
|
+
|
|
19
|
+
const left = (node.left ?? NIL) as RedBlackTreeNode<K, V>;
|
|
20
|
+
const right = (node.right ?? NIL) as RedBlackTreeNode<K, V>;
|
|
21
|
+
|
|
22
|
+
if (node.color === 'RED') {
|
|
23
|
+
expect(left.color).toBe('BLACK');
|
|
24
|
+
expect(right.color).toBe('BLACK');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
check(left, min, node.key as any);
|
|
28
|
+
check(right, node.key as any, max);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
check(root, undefined, undefined);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('RedBlackTree delete-fixup coverage', () => {
|
|
35
|
+
it('CLRS-style delete sequence exercises multiple fixup branches', () => {
|
|
36
|
+
// Classic CLRS insertion set.
|
|
37
|
+
const t = new RedBlackTree<number, number>();
|
|
38
|
+
for (const k of [7, 3, 18, 10, 22, 8, 11, 26]) t.set(k, k);
|
|
39
|
+
validateRedBlackTree(t);
|
|
40
|
+
|
|
41
|
+
// Deletes from CLRS examples / common RB delete discussions.
|
|
42
|
+
for (const k of [18, 11, 3, 10, 22, 26, 8, 7]) {
|
|
43
|
+
t.delete(k);
|
|
44
|
+
validateRedBlackTree(t);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
expect(t.size).toBe(0);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('deleting many black leaves triggers fixup in both left/right sibling orientations', () => {
|
|
51
|
+
const t = new RedBlackTree<number, number>();
|
|
52
|
+
// Build a reasonably balanced tree.
|
|
53
|
+
for (let i = 1; i <= 31; i++) t.set(i, i);
|
|
54
|
+
validateRedBlackTree(t);
|
|
55
|
+
|
|
56
|
+
// Delete odds then evens.
|
|
57
|
+
for (let i = 1; i <= 31; i += 2) {
|
|
58
|
+
t.delete(i);
|
|
59
|
+
validateRedBlackTree(t);
|
|
60
|
+
}
|
|
61
|
+
for (let i = 2; i <= 30; i += 2) {
|
|
62
|
+
t.delete(i);
|
|
63
|
+
validateRedBlackTree(t);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
expect(t.size).toBe(0);
|
|
67
|
+
});
|
|
68
|
+
});
|
package/test/unit/data-structures/binary-tree/red-black-tree.delete-successor.coverage.test.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { RedBlackTree } from '../../../../src';
|
|
2
|
+
|
|
3
|
+
describe('RedBlackTree delete() successor/transplant branch coverage', () => {
|
|
4
|
+
it('covers successor.parent === nodeToDelete with real replacementNode (sets replacement.parent = successor)', () => {
|
|
5
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
6
|
+
|
|
7
|
+
// Build:
|
|
8
|
+
// 10
|
|
9
|
+
// / \
|
|
10
|
+
// 5 15
|
|
11
|
+
// \
|
|
12
|
+
// 20
|
|
13
|
+
for (const k of [10, 5, 15, 20]) t.set(k, k);
|
|
14
|
+
|
|
15
|
+
const n20 = t.getNode(20)!;
|
|
16
|
+
expect(n20.parent?.key).toBe(15);
|
|
17
|
+
|
|
18
|
+
t.delete(10);
|
|
19
|
+
|
|
20
|
+
// successor should be 15; replacement should be 20 (real), and its parent should be set to successor.
|
|
21
|
+
expect(t.has(10)).toBe(false);
|
|
22
|
+
expect(t.has(5)).toBe(true);
|
|
23
|
+
expect(t.has(15)).toBe(true);
|
|
24
|
+
expect(t.has(20)).toBe(true);
|
|
25
|
+
|
|
26
|
+
expect(t.getNode(20)?.parent?.key).toBe(15);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('covers successor.parent === nodeToDelete with non-real replacementNode (skips replacement.parent assignment)', () => {
|
|
30
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
31
|
+
|
|
32
|
+
// Build:
|
|
33
|
+
// 10
|
|
34
|
+
// / \
|
|
35
|
+
// 5 15
|
|
36
|
+
// Successor is 15; successor.right is NIL (replacement non-real).
|
|
37
|
+
for (const k of [10, 5, 15]) t.set(k, k);
|
|
38
|
+
|
|
39
|
+
const s15 = t.getNode(15)!;
|
|
40
|
+
// Ensure right is NIL (not null), so replacementNode will be non-real and guard is exercised.
|
|
41
|
+
s15._right = t.NIL as any;
|
|
42
|
+
|
|
43
|
+
t.delete(10);
|
|
44
|
+
|
|
45
|
+
expect(t.has(10)).toBe(false);
|
|
46
|
+
expect(t.has(5)).toBe(true);
|
|
47
|
+
expect(t.has(15)).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('covers successor.parent !== nodeToDelete branch (transplant successor, then rewire successor.right)', () => {
|
|
51
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
52
|
+
|
|
53
|
+
// Build right subtree where left-most successor is deeper:
|
|
54
|
+
// 10
|
|
55
|
+
// / \
|
|
56
|
+
// 5 20
|
|
57
|
+
// / \
|
|
58
|
+
// 15 25
|
|
59
|
+
// /
|
|
60
|
+
// 13 (successor of 10)
|
|
61
|
+
for (const k of [10, 5, 20, 15, 25, 13]) t.set(k, k);
|
|
62
|
+
|
|
63
|
+
const s13 = t.getNode(13)!;
|
|
64
|
+
expect(s13.parent?.key).toBe(15);
|
|
65
|
+
|
|
66
|
+
t.delete(10);
|
|
67
|
+
|
|
68
|
+
expect(t.has(10)).toBe(false);
|
|
69
|
+
// successor (13) should now be in the deleted node's position and have right subtree attached.
|
|
70
|
+
const newRoot = (t as any)._root;
|
|
71
|
+
expect(newRoot?.key).toBe(13);
|
|
72
|
+
expect(t.has(20)).toBe(true);
|
|
73
|
+
expect(t.getNode(20)?.parent?.key).toBe(13);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { RedBlackTree } from '../../../../src';
|
|
2
|
+
|
|
3
|
+
describe('RedBlackTree internal factory coverage', () => {
|
|
4
|
+
it('_createLike default iterable branch returns empty like-kind', () => {
|
|
5
|
+
const t = new RedBlackTree<number, number>();
|
|
6
|
+
t.set(1, 1);
|
|
7
|
+
|
|
8
|
+
const like = (t as any)._createLike();
|
|
9
|
+
expect(like).toBeInstanceOf(RedBlackTree);
|
|
10
|
+
expect(like.size).toBe(0);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('_createInstance and _setRoot keep header.parent in sync', () => {
|
|
14
|
+
const t = new RedBlackTree<number, number>();
|
|
15
|
+
|
|
16
|
+
const inst = (t as any)._createInstance();
|
|
17
|
+
const NIL = (inst as any).NIL;
|
|
18
|
+
|
|
19
|
+
// new instance should have header.parent pointing at NIL
|
|
20
|
+
expect((inst as any)._header.parent).toBe(NIL);
|
|
21
|
+
|
|
22
|
+
// For coverage: explicitly set root to undefined and ensure header.parent remains NIL.
|
|
23
|
+
(inst as any)._setRoot(undefined);
|
|
24
|
+
expect((inst as any)._header.parent).toBe(NIL);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { RedBlackTree } from '../../../../src';
|
|
2
|
+
|
|
3
|
+
describe('RedBlackTree hint cache maintenance compare-update branches', () => {
|
|
4
|
+
it('direct attach left: updates min cache via compare(newKey,hMin.key) < 0 when hMin is real', () => {
|
|
5
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
6
|
+
for (const k of [50, 10, 100, 60]) t.set(k, k);
|
|
7
|
+
const hint = t.getNode(60)!;
|
|
8
|
+
|
|
9
|
+
// Ensure direct attach path.
|
|
10
|
+
hint._left = t.NIL as any;
|
|
11
|
+
|
|
12
|
+
const oldMin = (t as any)._header._left.key;
|
|
13
|
+
expect(oldMin).toBe(10);
|
|
14
|
+
|
|
15
|
+
t.setWithHintNode(5, 5, hint);
|
|
16
|
+
expect((t as any)._header._left.key).toBe(5);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('direct attach right: updates max cache via compare(newKey,hMax.key) > 0 when hMax is real', () => {
|
|
20
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
21
|
+
for (const k of [50, 10, 100, 40]) t.set(k, k);
|
|
22
|
+
const hint = t.getNode(40)!;
|
|
23
|
+
|
|
24
|
+
// Ensure direct attach path.
|
|
25
|
+
hint._right = t.NIL as any;
|
|
26
|
+
|
|
27
|
+
const oldMax = (t as any)._header._right.key;
|
|
28
|
+
expect(oldMax).toBe(100);
|
|
29
|
+
|
|
30
|
+
t.setWithHintNode(200, 200, hint);
|
|
31
|
+
expect((t as any)._header._right.key).toBe(200);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('pred.right attach: can update min/max cache via compare checks when inserting a new extreme', () => {
|
|
35
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
36
|
+
for (const k of [50, 10, 100, 60]) t.set(k, k);
|
|
37
|
+
const hint = t.getNode(60)!;
|
|
38
|
+
const pred = t.getNode(50)!;
|
|
39
|
+
|
|
40
|
+
// Force pred.right attach path.
|
|
41
|
+
pred.parent = hint as any;
|
|
42
|
+
pred._right = t.NIL as any;
|
|
43
|
+
hint._left = pred as any;
|
|
44
|
+
|
|
45
|
+
t.setWithHintNode(5, 5, hint);
|
|
46
|
+
expect((t as any)._header._left.key).toBe(5);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('succ.left attach: can update min/max cache via compare checks when inserting a new extreme', () => {
|
|
50
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
51
|
+
for (const k of [50, 10, 100, 40]) t.set(k, k);
|
|
52
|
+
const hint = t.getNode(40)!;
|
|
53
|
+
const succ = t.getNode(50)!;
|
|
54
|
+
|
|
55
|
+
succ.parent = hint as any;
|
|
56
|
+
succ._left = t.NIL as any;
|
|
57
|
+
hint._right = succ as any;
|
|
58
|
+
|
|
59
|
+
t.setWithHintNode(200, 200, hint);
|
|
60
|
+
expect((t as any)._header._right.key).toBe(200);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('_insertFixup: breaks when parent is RED but grandparent is missing (gp undefined)', () => {
|
|
64
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
65
|
+
const z = (t as any).createNode(1, 1);
|
|
66
|
+
const p = (t as any).createNode(0, 0);
|
|
67
|
+
p.color = 'RED';
|
|
68
|
+
p.parent = undefined;
|
|
69
|
+
z.parent = p;
|
|
70
|
+
|
|
71
|
+
// Should hit `if (!gp) break;` and return without throwing.
|
|
72
|
+
expect(() => (t as any)._insertFixup(z)).not.toThrow();
|
|
73
|
+
});
|
|
74
|
+
});
|
package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-no-update.coverage.test.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { RedBlackTree } from '../../../../src';
|
|
2
|
+
|
|
3
|
+
describe('RedBlackTree setWithHintNode cache-maintenance no-update branches', () => {
|
|
4
|
+
it('direct attach does not update min/max caches when inserting within existing extremes', () => {
|
|
5
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
6
|
+
// Establish extremes.
|
|
7
|
+
for (const k of [50, 1, 100]) t.set(k, k);
|
|
8
|
+
|
|
9
|
+
const hint = t.getNode(50)!;
|
|
10
|
+
expect((t as any)._header._left.key).toBe(1);
|
|
11
|
+
expect((t as any)._header._right.key).toBe(100);
|
|
12
|
+
|
|
13
|
+
// direct attach left of hint, but key is not new min/max
|
|
14
|
+
const n40 = t.setWithHintNode(40, 40, hint);
|
|
15
|
+
expect(n40?.key).toBe(40);
|
|
16
|
+
expect((t as any)._header._left.key).toBe(1);
|
|
17
|
+
expect((t as any)._header._right.key).toBe(100);
|
|
18
|
+
|
|
19
|
+
// direct attach right of hint, but still within extremes
|
|
20
|
+
const n60 = t.setWithHintNode(60, 60, hint);
|
|
21
|
+
expect(n60?.key).toBe(60);
|
|
22
|
+
expect((t as any)._header._left.key).toBe(1);
|
|
23
|
+
expect((t as any)._header._right.key).toBe(100);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('pred.right / succ.left attach does not update min/max caches when inserting within existing extremes', () => {
|
|
27
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
28
|
+
for (const k of [50, 1, 100, 40, 60]) t.set(k, k);
|
|
29
|
+
const hint = t.getNode(50)!;
|
|
30
|
+
|
|
31
|
+
// pred.right attach: ensure hint.left is real so we take pred path.
|
|
32
|
+
expect(t.getNode(40)).toBeTruthy();
|
|
33
|
+
const n45 = t.setWithHintNode(45, 45, hint);
|
|
34
|
+
expect(n45?.key).toBe(45);
|
|
35
|
+
|
|
36
|
+
// succ.left attach: ensure hint.right is real so we take succ path.
|
|
37
|
+
expect(t.getNode(60)).toBeTruthy();
|
|
38
|
+
const n55 = t.setWithHintNode(55, 55, hint);
|
|
39
|
+
expect(n55?.key).toBe(55);
|
|
40
|
+
|
|
41
|
+
expect((t as any)._header._left.key).toBe(1);
|
|
42
|
+
expect((t as any)._header._right.key).toBe(100);
|
|
43
|
+
});
|
|
44
|
+
});
|
package/test/unit/data-structures/binary-tree/red-black-tree.hint-cache-nullish.coverage.test.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { RedBlackTree } from '../../../../src';
|
|
2
|
+
|
|
3
|
+
describe('RedBlackTree hint cache maintenance with nullish/NIL header caches', () => {
|
|
4
|
+
it('direct attach left/right: when header caches are NIL, hMin/hMax short-circuit branches initialize caches', () => {
|
|
5
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
6
|
+
t.set(10, 10);
|
|
7
|
+
const hint = t.getNode(10)!;
|
|
8
|
+
|
|
9
|
+
// Corrupt caches to NIL so (hMin===NIL || ...) short-circuits true.
|
|
10
|
+
(t as any)._header._left = t.NIL;
|
|
11
|
+
(t as any)._header._right = t.NIL;
|
|
12
|
+
|
|
13
|
+
const n5 = t.setWithHintNode(5, 5, hint)!;
|
|
14
|
+
expect((t as any)._header._left).toBe(n5);
|
|
15
|
+
expect((t as any)._header._right).toBe(n5);
|
|
16
|
+
|
|
17
|
+
// Corrupt again and attach on the other side.
|
|
18
|
+
(t as any)._header._left = t.NIL;
|
|
19
|
+
(t as any)._header._right = t.NIL;
|
|
20
|
+
|
|
21
|
+
const n15 = t.setWithHintNode(15, 15, hint)!;
|
|
22
|
+
expect((t as any)._header._left).toBe(n15);
|
|
23
|
+
expect((t as any)._header._right).toBe(n15);
|
|
24
|
+
|
|
25
|
+
// Repair to real extremes to avoid leakage.
|
|
26
|
+
const root = (t as any)._root;
|
|
27
|
+
(t as any)._setMinCache(t.isRealNode(root) ? t.getLeftMost((n: any) => n, root) : undefined);
|
|
28
|
+
(t as any)._setMaxCache(t.isRealNode(root) ? t.getRightMost((n: any) => n, root) : undefined);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('pred.right / succ.left attach: when header caches are NIL, hMin/hMax short-circuit branches initialize caches', () => {
|
|
32
|
+
const t = new RedBlackTree<number, number>([], { isMapMode: false });
|
|
33
|
+
t.set(10, 10);
|
|
34
|
+
const hint = t.getNode(10)!;
|
|
35
|
+
|
|
36
|
+
// Create real left/right so we take pred/succ attach paths.
|
|
37
|
+
t.setWithHintNode(5, 5, hint);
|
|
38
|
+
t.setWithHintNode(15, 15, hint);
|
|
39
|
+
|
|
40
|
+
// pred.right attach, with caches NIL.
|
|
41
|
+
(t as any)._header._left = t.NIL;
|
|
42
|
+
(t as any)._header._right = t.NIL;
|
|
43
|
+
|
|
44
|
+
const n7 = t.setWithHintNode(7, 7, hint)!;
|
|
45
|
+
expect((t as any)._header._left).toBe(n7);
|
|
46
|
+
expect((t as any)._header._right).toBe(n7);
|
|
47
|
+
|
|
48
|
+
// succ.left attach, with caches NIL.
|
|
49
|
+
(t as any)._header._left = t.NIL;
|
|
50
|
+
(t as any)._header._right = t.NIL;
|
|
51
|
+
|
|
52
|
+
const n12 = t.setWithHintNode(12, 12, hint)!;
|
|
53
|
+
expect((t as any)._header._left).toBe(n12);
|
|
54
|
+
expect((t as any)._header._right).toBe(n12);
|
|
55
|
+
|
|
56
|
+
// Repair to real extremes.
|
|
57
|
+
const root = (t as any)._root;
|
|
58
|
+
(t as any)._setMinCache(t.isRealNode(root) ? t.getLeftMost((n: any) => n, root) : undefined);
|
|
59
|
+
(t as any)._setMaxCache(t.isRealNode(root) ? t.getRightMost((n: any) => n, root) : undefined);
|
|
60
|
+
});
|
|
61
|
+
});
|