binary-tree-typed 2.4.5 → 2.5.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/README.md +0 -84
- package/dist/cjs/index.cjs +1476 -404
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +1473 -401
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +1476 -404
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +1473 -401
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/base/index.d.ts +1 -0
- package/dist/types/data-structures/base/iterable-element-base.d.ts +1 -1
- package/dist/types/data-structures/base/iterable-entry-base.d.ts +8 -8
- package/dist/types/data-structures/base/linear-base.d.ts +3 -3
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +380 -51
- package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +487 -147
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +956 -80
- package/dist/types/data-structures/binary-tree/bst.d.ts +816 -29
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +610 -31
- package/dist/types/data-structures/binary-tree/segment-tree.d.ts +326 -135
- package/dist/types/data-structures/binary-tree/tree-map.d.ts +3781 -6
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +3607 -201
- package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +2874 -65
- package/dist/types/data-structures/binary-tree/tree-set.d.ts +3528 -6
- package/dist/types/data-structures/graph/abstract-graph.d.ts +4 -4
- package/dist/types/data-structures/graph/directed-graph.d.ts +429 -47
- package/dist/types/data-structures/graph/map-graph.d.ts +59 -1
- package/dist/types/data-structures/graph/undirected-graph.d.ts +393 -59
- package/dist/types/data-structures/hash/hash-map.d.ts +473 -89
- package/dist/types/data-structures/heap/heap.d.ts +581 -99
- package/dist/types/data-structures/heap/max-heap.d.ts +46 -0
- package/dist/types/data-structures/heap/min-heap.d.ts +59 -0
- package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +646 -47
- package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +596 -68
- package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +793 -12
- package/dist/types/data-structures/matrix/matrix.d.ts +499 -0
- package/dist/types/data-structures/priority-queue/max-priority-queue.d.ts +57 -0
- package/dist/types/data-structures/priority-queue/min-priority-queue.d.ts +60 -0
- package/dist/types/data-structures/priority-queue/priority-queue.d.ts +60 -0
- package/dist/types/data-structures/queue/deque.d.ts +593 -71
- package/dist/types/data-structures/queue/queue.d.ts +463 -42
- package/dist/types/data-structures/stack/stack.d.ts +384 -32
- package/dist/types/data-structures/trie/trie.d.ts +470 -48
- package/dist/types/interfaces/graph.d.ts +1 -1
- package/dist/types/types/common.d.ts +2 -2
- package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -1
- package/dist/types/types/data-structures/heap/heap.d.ts +1 -0
- package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +1 -4
- package/dist/types/types/data-structures/priority-queue/priority-queue.d.ts +1 -0
- package/dist/types/types/utils/validate-type.d.ts +4 -4
- package/dist/umd/binary-tree-typed.js +1469 -397
- package/dist/umd/binary-tree-typed.js.map +1 -1
- package/dist/umd/binary-tree-typed.min.js +5 -5
- package/dist/umd/binary-tree-typed.min.js.map +1 -1
- package/package.json +2 -2
- package/src/data-structures/base/index.ts +1 -0
- package/src/data-structures/base/iterable-element-base.ts +4 -5
- package/src/data-structures/base/iterable-entry-base.ts +8 -8
- package/src/data-structures/base/linear-base.ts +3 -3
- package/src/data-structures/binary-tree/avl-tree.ts +386 -51
- package/src/data-structures/binary-tree/binary-indexed-tree.ts +596 -247
- package/src/data-structures/binary-tree/binary-tree.ts +956 -81
- package/src/data-structures/binary-tree/bst.ts +840 -35
- package/src/data-structures/binary-tree/red-black-tree.ts +689 -97
- package/src/data-structures/binary-tree/segment-tree.ts +498 -249
- package/src/data-structures/binary-tree/tree-map.ts +3784 -7
- package/src/data-structures/binary-tree/tree-multi-map.ts +3614 -211
- package/src/data-structures/binary-tree/tree-multi-set.ts +2874 -65
- package/src/data-structures/binary-tree/tree-set.ts +3531 -10
- package/src/data-structures/graph/abstract-graph.ts +4 -4
- package/src/data-structures/graph/directed-graph.ts +429 -47
- package/src/data-structures/graph/map-graph.ts +59 -1
- package/src/data-structures/graph/undirected-graph.ts +393 -59
- package/src/data-structures/hash/hash-map.ts +476 -92
- package/src/data-structures/heap/heap.ts +581 -99
- package/src/data-structures/heap/max-heap.ts +46 -0
- package/src/data-structures/heap/min-heap.ts +59 -0
- package/src/data-structures/linked-list/doubly-linked-list.ts +646 -47
- package/src/data-structures/linked-list/singly-linked-list.ts +596 -68
- package/src/data-structures/linked-list/skip-linked-list.ts +1067 -90
- package/src/data-structures/matrix/matrix.ts +584 -12
- package/src/data-structures/priority-queue/max-priority-queue.ts +57 -0
- package/src/data-structures/priority-queue/min-priority-queue.ts +60 -0
- package/src/data-structures/priority-queue/priority-queue.ts +60 -0
- package/src/data-structures/queue/deque.ts +592 -70
- package/src/data-structures/queue/queue.ts +463 -42
- package/src/data-structures/stack/stack.ts +384 -32
- package/src/data-structures/trie/trie.ts +470 -48
- package/src/interfaces/graph.ts +1 -1
- package/src/types/common.ts +2 -2
- package/src/types/data-structures/binary-tree/segment-tree.ts +1 -1
- package/src/types/data-structures/heap/heap.ts +1 -0
- package/src/types/data-structures/linked-list/skip-linked-list.ts +2 -1
- package/src/types/data-structures/priority-queue/priority-queue.ts +1 -0
- package/src/types/utils/validate-type.ts +4 -4
|
@@ -2,323 +2,572 @@
|
|
|
2
2
|
* data-structure-typed
|
|
3
3
|
*
|
|
4
4
|
* @author Pablo Zeng
|
|
5
|
-
* @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
|
|
6
5
|
* @license MIT License
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
export type SegmentTreeOptions<E> = {
|
|
9
|
+
merger: (a: E, b: E) => E;
|
|
10
|
+
identity: E;
|
|
11
|
+
};
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Generic Segment Tree with flat array internals.
|
|
15
|
+
*
|
|
16
|
+
* Supports any associative merge operation (sum, min, max, gcd, etc.).
|
|
17
|
+
* Reference: AtCoder Library segtree<S, op, e>.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const sumTree = SegmentTree.sum([1, 2, 3, 4, 5]);
|
|
22
|
+
* sumTree.query(1, 3); // 9 (2+3+4)
|
|
23
|
+
* sumTree.update(2, 10); // [1, 2, 10, 4, 5]
|
|
24
|
+
* sumTree.query(1, 3); // 16 (2+10+4)
|
|
25
|
+
*
|
|
26
|
+
* const minTree = SegmentTree.min([5, 2, 8, 1, 9]);
|
|
27
|
+
* minTree.query(0, 4); // 1
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export class SegmentTree<E = number> implements Iterable<E> {
|
|
31
|
+
protected readonly _merger: (a: E, b: E) => E;
|
|
32
|
+
protected readonly _identity: E;
|
|
33
|
+
protected _n: number; // number of leaf elements
|
|
34
|
+
protected _tree: E[]; // flat array, 1-indexed, size 2*_size
|
|
35
|
+
protected _treeSize: number; // internal tree size (next power of 2 >= _n)
|
|
36
|
+
|
|
37
|
+
constructor(elements: E[], options: SegmentTreeOptions<E>) {
|
|
38
|
+
this._merger = options.merger;
|
|
39
|
+
this._identity = options.identity;
|
|
40
|
+
this._n = elements.length;
|
|
41
|
+
|
|
42
|
+
// Round up to next power of 2
|
|
43
|
+
this._treeSize = 1;
|
|
44
|
+
while (this._treeSize < this._n) this._treeSize <<= 1;
|
|
45
|
+
|
|
46
|
+
// Allocate and fill with identity
|
|
47
|
+
this._tree = new Array(2 * this._treeSize).fill(this._identity);
|
|
48
|
+
|
|
49
|
+
// Place elements in leaves
|
|
50
|
+
for (let i = 0; i < this._n; i++) {
|
|
51
|
+
this._tree[this._treeSize + i] = elements[i];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Build internal nodes bottom-up
|
|
55
|
+
for (let i = this._treeSize - 1; i >= 1; i--) {
|
|
56
|
+
this._tree[i] = this._merger(this._tree[2 * i], this._tree[2 * i + 1]);
|
|
57
|
+
}
|
|
28
58
|
}
|
|
29
59
|
|
|
30
|
-
|
|
60
|
+
// ─── Convenience factories ─────────────────────────────────
|
|
31
61
|
|
|
32
62
|
/**
|
|
33
|
-
*
|
|
34
|
-
* @
|
|
63
|
+
* Create a sum segment tree.
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const st = SegmentTree.sum([1, 2, 3, 4, 5]);
|
|
67
|
+
* st.query(0, 2); // 6 (1+2+3)
|
|
68
|
+
* st.update(1, 10);
|
|
69
|
+
* st.query(0, 2); // 14 (1+10+3)
|
|
70
|
+
* ```
|
|
35
71
|
*/
|
|
36
|
-
|
|
37
|
-
return
|
|
72
|
+
static sum(elements: number[]): SegmentTree<number> {
|
|
73
|
+
return new SegmentTree<number>(elements, {
|
|
74
|
+
merger: (a, b) => a + b,
|
|
75
|
+
identity: 0
|
|
76
|
+
});
|
|
38
77
|
}
|
|
39
78
|
|
|
40
79
|
/**
|
|
41
|
-
*
|
|
42
|
-
|
|
80
|
+
* Create a min segment tree.
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
* @example
|
|
114
|
+
* // Temperature monitoring with range queries
|
|
115
|
+
* // Hourly temperatures for a day (24 readings)
|
|
116
|
+
* const temps = [18, 17, 16, 15, 16, 18, 21, 24, 27, 29, 31, 32, 33, 32, 31, 29, 27, 25, 23, 21, 20, 19, 18, 17];
|
|
117
|
+
* const tree = SegmentTree.sum(temps);
|
|
118
|
+
*
|
|
119
|
+
* // Average temperature during work hours (9-17)
|
|
120
|
+
* const workSum = tree.query(9, 17);
|
|
121
|
+
* console.log(workSum / 9); // toBeCloseTo;
|
|
122
|
+
*
|
|
123
|
+
* // Sum of morning temps (6-11)
|
|
124
|
+
* console.log(tree.query(6, 11)); // 164;
|
|
43
125
|
*/
|
|
44
|
-
|
|
45
|
-
|
|
126
|
+
static min(elements: number[]): SegmentTree<number> {
|
|
127
|
+
return new SegmentTree<number>(elements, {
|
|
128
|
+
merger: (a, b) => Math.min(a, b),
|
|
129
|
+
identity: Infinity
|
|
130
|
+
});
|
|
46
131
|
}
|
|
47
132
|
|
|
48
|
-
protected _end = 0;
|
|
49
|
-
|
|
50
133
|
/**
|
|
51
|
-
*
|
|
52
|
-
* @
|
|
134
|
+
* Create a max segment tree.
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* const st = SegmentTree.max([3, 1, 4, 1, 5]);
|
|
138
|
+
* st.query(1, 4); // 5
|
|
139
|
+
* ```
|
|
53
140
|
*/
|
|
54
|
-
|
|
55
|
-
return
|
|
141
|
+
static max(elements: number[]): SegmentTree<number> {
|
|
142
|
+
return new SegmentTree<number>(elements, {
|
|
143
|
+
merger: (a, b) => Math.max(a, b),
|
|
144
|
+
identity: -Infinity
|
|
145
|
+
});
|
|
56
146
|
}
|
|
57
147
|
|
|
148
|
+
// ─── Core operations ───────────────────────────────────────
|
|
149
|
+
|
|
58
150
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
|
|
151
|
+
* Point update: set element at index to value.
|
|
152
|
+
* Time: O(log n)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
* @example
|
|
186
|
+
* // Dynamic range sum with updates
|
|
187
|
+
* // Monthly revenue data (in thousands)
|
|
188
|
+
* const revenue = [120, 95, 140, 110, 85, 130, 150, 100, 160, 125, 90, 175];
|
|
189
|
+
* const tree = SegmentTree.sum(revenue);
|
|
190
|
+
*
|
|
191
|
+
* // Q1 revenue (Jan-Mar)
|
|
192
|
+
* console.log(tree.query(0, 2)); // 355;
|
|
193
|
+
*
|
|
194
|
+
* // Update March revenue from 140 to 200
|
|
195
|
+
* tree.update(2, 200);
|
|
196
|
+
*
|
|
197
|
+
* // Q1 revenue after update
|
|
198
|
+
* console.log(tree.query(0, 2)); // 415;
|
|
199
|
+
*
|
|
200
|
+
* // H1 revenue (Jan-Jun)
|
|
201
|
+
* console.log(tree.query(0, 5)); // 740;
|
|
62
202
|
*/
|
|
63
|
-
|
|
64
|
-
this.
|
|
65
|
-
}
|
|
203
|
+
update(index: number, value: E): void {
|
|
204
|
+
if (index < 0 || index >= this._n) return;
|
|
66
205
|
|
|
67
|
-
|
|
206
|
+
let pos = this._treeSize + index;
|
|
207
|
+
this._tree[pos] = value;
|
|
68
208
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
209
|
+
// Propagate up
|
|
210
|
+
pos >>= 1;
|
|
211
|
+
while (pos >= 1) {
|
|
212
|
+
this._tree[pos] = this._merger(this._tree[2 * pos], this._tree[2 * pos + 1]);
|
|
213
|
+
pos >>= 1;
|
|
214
|
+
}
|
|
75
215
|
}
|
|
76
216
|
|
|
77
217
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
|
|
218
|
+
* Range query: returns merger result over [start, end] (inclusive).
|
|
219
|
+
* Time: O(log n)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
* @example
|
|
253
|
+
* // Range sum query on an array
|
|
254
|
+
* const tree = SegmentTree.sum([1, 3, 5, 7, 9, 11]);
|
|
255
|
+
*
|
|
256
|
+
* // Query sum of range [1, 3] → 3 + 5 + 7 = 15
|
|
257
|
+
* console.log(tree.query(1, 3)); // 15;
|
|
258
|
+
*
|
|
259
|
+
* // Query entire range
|
|
260
|
+
* console.log(tree.query(0, 5)); // 36;
|
|
261
|
+
*
|
|
262
|
+
* // Query single element
|
|
263
|
+
* console.log(tree.query(2, 2)); // 5;
|
|
81
264
|
*/
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
265
|
+
query(start: number, end: number): E {
|
|
266
|
+
if (start < 0) start = 0;
|
|
267
|
+
if (end >= this._n) end = this._n - 1;
|
|
268
|
+
if (start > end) return this._identity;
|
|
269
|
+
|
|
270
|
+
let resultLeft = this._identity;
|
|
271
|
+
let resultRight = this._identity;
|
|
272
|
+
let left = this._treeSize + start;
|
|
273
|
+
let right = this._treeSize + end + 1; // exclusive
|
|
274
|
+
|
|
275
|
+
while (left < right) {
|
|
276
|
+
if (left & 1) {
|
|
277
|
+
resultLeft = this._merger(resultLeft, this._tree[left]);
|
|
278
|
+
left++;
|
|
279
|
+
}
|
|
280
|
+
if (right & 1) {
|
|
281
|
+
right--;
|
|
282
|
+
resultRight = this._merger(this._tree[right], resultRight);
|
|
283
|
+
}
|
|
284
|
+
left >>= 1;
|
|
285
|
+
right >>= 1;
|
|
286
|
+
}
|
|
87
287
|
|
|
88
|
-
|
|
89
|
-
* The function returns the value of the sum property.
|
|
90
|
-
* @returns The method is returning the value of the variable `_sum`.
|
|
91
|
-
*/
|
|
92
|
-
get sum(): number {
|
|
93
|
-
return this._sum;
|
|
288
|
+
return this._merger(resultLeft, resultRight);
|
|
94
289
|
}
|
|
95
290
|
|
|
96
291
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
292
|
+
* Get element at index.
|
|
293
|
+
* Time: O(1)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
* @example
|
|
327
|
+
* // Point access on segment tree
|
|
328
|
+
* const st = SegmentTree.sum([10, 20, 30, 40]);
|
|
329
|
+
* console.log(st.get(0)); // 10;
|
|
330
|
+
* console.log(st.get(2)); // 30;
|
|
99
331
|
*/
|
|
100
|
-
|
|
101
|
-
this.
|
|
332
|
+
get(index: number): E {
|
|
333
|
+
if (index < 0 || index >= this._n) return this._identity;
|
|
334
|
+
return this._tree[this._treeSize + index];
|
|
102
335
|
}
|
|
103
336
|
|
|
104
|
-
|
|
337
|
+
// ─── Binary search on tree (ACL-style) ─────────────────────
|
|
105
338
|
|
|
106
339
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
340
|
+
* Find the largest r such that predicate(query(left, r)) is true.
|
|
341
|
+
* Returns left-1 if predicate(identity) is false.
|
|
342
|
+
* Returns n-1 if predicate holds for the entire range [left, n-1].
|
|
343
|
+
* Time: O(log n)
|
|
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
|
+
* @example
|
|
377
|
+
* // Find rightmost position where predicate holds
|
|
378
|
+
* // Prefix sums: find the rightmost index where prefix sum < 10
|
|
379
|
+
* const st = SegmentTree.sum([3, 1, 4, 1, 5]);
|
|
380
|
+
* // maxRight(0, sum => sum < 10) — prefix [3,4,8,9,14]
|
|
381
|
+
* // sum < 10 holds through index 3 (prefix=9), fails at 4 (prefix=14)
|
|
382
|
+
* const result = st.maxRight(0, sum => sum < 10);
|
|
383
|
+
* console.log(result); // 3;
|
|
110
384
|
*/
|
|
111
|
-
|
|
112
|
-
return this.
|
|
113
|
-
|
|
385
|
+
maxRight(left: number, predicate: (segValue: E) => boolean): number {
|
|
386
|
+
if (left >= this._n) return this._n - 1;
|
|
387
|
+
|
|
388
|
+
let acc = this._identity;
|
|
389
|
+
if (!predicate(acc)) return left - 1;
|
|
390
|
+
|
|
391
|
+
let pos = this._treeSize + left;
|
|
392
|
+
|
|
393
|
+
// Go up while we're a right child or predicate still holds
|
|
394
|
+
while (true) {
|
|
395
|
+
// Find the lowest relevant node
|
|
396
|
+
while (pos < this._treeSize) {
|
|
397
|
+
// Try going left
|
|
398
|
+
const combined = this._merger(acc, this._tree[2 * pos]);
|
|
399
|
+
if (predicate(combined)) {
|
|
400
|
+
acc = combined;
|
|
401
|
+
pos = 2 * pos + 1; // go right
|
|
402
|
+
} else {
|
|
403
|
+
pos = 2 * pos; // go left (dig deeper)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
114
406
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this._left = value;
|
|
122
|
-
}
|
|
407
|
+
// At leaf level
|
|
408
|
+
const combined = this._merger(acc, this._tree[pos]);
|
|
409
|
+
if (!predicate(combined)) {
|
|
410
|
+
return pos - this._treeSize - 1;
|
|
411
|
+
}
|
|
412
|
+
acc = combined;
|
|
123
413
|
|
|
124
|
-
|
|
414
|
+
// Move to next segment
|
|
415
|
+
pos++;
|
|
416
|
+
// Check if we've gone past the end
|
|
417
|
+
if (pos - this._treeSize >= this._n) return this._n - 1;
|
|
125
418
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
419
|
+
// Go up while we're a right child
|
|
420
|
+
while (pos > 1 && (pos & 1) === 0) {
|
|
421
|
+
pos >>= 1;
|
|
422
|
+
}
|
|
423
|
+
/* istanbul ignore next -- defensive: pos===1 unreachable when _n < _treeSize guard above catches exit */
|
|
424
|
+
if (pos === 1) return this._n - 1;
|
|
425
|
+
}
|
|
132
426
|
}
|
|
133
427
|
|
|
134
428
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
429
|
+
* Find the smallest l such that predicate(query(l, right)) is true.
|
|
430
|
+
* Returns right+1 if predicate(identity) is false.
|
|
431
|
+
* Returns 0 if predicate holds for the entire range [0, right].
|
|
432
|
+
* Time: O(log n)
|
|
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
|
+
* @example
|
|
466
|
+
* // Find leftmost position where predicate holds
|
|
467
|
+
* const st = SegmentTree.sum([3, 1, 4, 1, 5]);
|
|
468
|
+
* // minLeft(5, sum => sum < 7) — suffix sums from right
|
|
469
|
+
* // From right: [5]=5 < 7, [1,5]=6 < 7, [4,1,5]=10 ≥ 7
|
|
470
|
+
* const result = st.minLeft(5, sum => sum < 7);
|
|
471
|
+
* console.log(result); // 3;
|
|
139
472
|
*/
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
473
|
+
minLeft(right: number, predicate: (segValue: E) => boolean): number {
|
|
474
|
+
if (right < 0) return 0;
|
|
475
|
+
if (right >= this._n) right = this._n - 1;
|
|
144
476
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
* The constructor initializes the values, start, end, and root properties of an object.
|
|
148
|
-
* @param {number[]} values - An array of numbers that will be used to build a binary search tree.
|
|
149
|
-
* @param {number} [start] - The `start` parameter is the index of the first element in the `values` array that should
|
|
150
|
-
* be included in the range. If no value is provided for `start`, it defaults to 0, which means the range starts from
|
|
151
|
-
* the beginning of the array.
|
|
152
|
-
* @param {number} [end] - The "end" parameter is the index of the last element in the "values" array that should be
|
|
153
|
-
* included in the range. If not provided, it defaults to the index of the last element in the "values" array.
|
|
154
|
-
*/
|
|
155
|
-
constructor(values: number[], start?: number, end?: number) {
|
|
156
|
-
start = start || 0;
|
|
157
|
-
end = end || values.length - 1;
|
|
158
|
-
this._values = values;
|
|
159
|
-
this._start = start;
|
|
160
|
-
this._end = end;
|
|
161
|
-
|
|
162
|
-
if (values.length > 0) {
|
|
163
|
-
this._root = this.build(start, end);
|
|
164
|
-
} else {
|
|
165
|
-
this._root = undefined;
|
|
166
|
-
this._values = [];
|
|
167
|
-
}
|
|
168
|
-
}
|
|
477
|
+
let acc = this._identity;
|
|
478
|
+
if (!predicate(acc)) return right + 1;
|
|
169
479
|
|
|
170
|
-
|
|
480
|
+
let pos = this._treeSize + right;
|
|
171
481
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
482
|
+
while (true) {
|
|
483
|
+
while (pos < this._treeSize) {
|
|
484
|
+
const combined = this._merger(this._tree[2 * pos + 1], acc);
|
|
485
|
+
if (predicate(combined)) {
|
|
486
|
+
acc = combined;
|
|
487
|
+
pos = 2 * pos; // go left
|
|
488
|
+
} else {
|
|
489
|
+
pos = 2 * pos + 1; // go right (dig deeper)
|
|
490
|
+
}
|
|
491
|
+
}
|
|
179
492
|
|
|
180
|
-
|
|
493
|
+
const combined = this._merger(this._tree[pos], acc);
|
|
494
|
+
if (!predicate(combined)) {
|
|
495
|
+
return pos - this._treeSize + 1;
|
|
496
|
+
}
|
|
497
|
+
acc = combined;
|
|
181
498
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
499
|
+
// Move to previous segment
|
|
500
|
+
if (pos === this._treeSize) return 0;
|
|
501
|
+
pos--;
|
|
502
|
+
|
|
503
|
+
// Go up while we're a left child
|
|
504
|
+
while (pos > 1 && (pos & 1) === 1) {
|
|
505
|
+
pos >>= 1;
|
|
506
|
+
}
|
|
507
|
+
/* istanbul ignore next -- defensive: pos===1 unreachable when _treeSize guard above catches exit */
|
|
508
|
+
if (pos === 1) return 0;
|
|
509
|
+
}
|
|
188
510
|
}
|
|
189
511
|
|
|
190
|
-
|
|
512
|
+
// ─── Standard interface ────────────────────────────────────
|
|
191
513
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
* @returns The value of the protected property `_end`.
|
|
195
|
-
*/
|
|
196
|
-
get end(): number {
|
|
197
|
-
return this._end;
|
|
514
|
+
get size(): number {
|
|
515
|
+
return this._n;
|
|
198
516
|
}
|
|
199
517
|
|
|
200
|
-
|
|
518
|
+
isEmpty(): boolean {
|
|
519
|
+
return this._n === 0;
|
|
520
|
+
}
|
|
201
521
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return
|
|
522
|
+
clone(): SegmentTree<E> {
|
|
523
|
+
const elements: E[] = [];
|
|
524
|
+
for (let i = 0; i < this._n; i++) {
|
|
525
|
+
elements.push(this._tree[this._treeSize + i]);
|
|
526
|
+
}
|
|
527
|
+
return new SegmentTree<E>(elements, {
|
|
528
|
+
merger: this._merger,
|
|
529
|
+
identity: this._identity
|
|
530
|
+
});
|
|
208
531
|
}
|
|
209
532
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
* building the segment tree.
|
|
215
|
-
* @param {number} end - The "end" parameter represents the ending index of the segment or range for which we want to
|
|
216
|
-
* build a segment tree.
|
|
217
|
-
* @returns a SegmentTreeNode object.
|
|
218
|
-
*/
|
|
219
|
-
build(start: number, end: number): SegmentTreeNode {
|
|
220
|
-
if (start > end) {
|
|
221
|
-
return new SegmentTreeNode(start, end, 0);
|
|
533
|
+
toArray(): E[] {
|
|
534
|
+
const result: E[] = [];
|
|
535
|
+
for (let i = 0; i < this._n; i++) {
|
|
536
|
+
result.push(this._tree[this._treeSize + i]);
|
|
222
537
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const mid = start + Math.floor((end - start) / 2);
|
|
226
|
-
const left = this.build(start, mid);
|
|
227
|
-
const right = this.build(mid + 1, end);
|
|
228
|
-
const cur = new SegmentTreeNode(start, end, left.sum + right.sum);
|
|
229
|
-
cur.left = left;
|
|
230
|
-
cur.right = right;
|
|
231
|
-
return cur;
|
|
538
|
+
return result;
|
|
232
539
|
}
|
|
233
540
|
|
|
234
541
|
/**
|
|
235
|
-
*
|
|
236
|
-
* @param {number} index - The index parameter represents the index of the node in the segment tree that needs to be
|
|
237
|
-
* updated.
|
|
238
|
-
* @param {number} sum - The `sum` parameter represents the new value that should be assigned to the `sum` property of
|
|
239
|
-
* the `SegmentTreeNode` at the specified `index`.
|
|
240
|
-
* @param {SegmentTreeNodeVal} [value] - The `value` parameter is an optional value that can be assigned to the `value`
|
|
241
|
-
* property of the `SegmentTreeNode` object. It is not currently used in the code, but you can uncomment the line `//
|
|
242
|
-
* cur.value = value;` and pass a value for `value` in the
|
|
243
|
-
* @returns The function does not return anything.
|
|
542
|
+
* Iterates over leaf values in index order.
|
|
244
543
|
*/
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (index <= mid) {
|
|
258
|
-
if (cur.left) {
|
|
259
|
-
dfs(cur.left, index, sum, value);
|
|
544
|
+
[Symbol.iterator](): IterableIterator<E> {
|
|
545
|
+
const tree = this._tree;
|
|
546
|
+
const treeSize = this._treeSize;
|
|
547
|
+
const n = this._n;
|
|
548
|
+
let i = 0;
|
|
549
|
+
return {
|
|
550
|
+
[Symbol.iterator]() {
|
|
551
|
+
return this;
|
|
552
|
+
},
|
|
553
|
+
next(): IteratorResult<E> {
|
|
554
|
+
if (i < n) {
|
|
555
|
+
return { value: tree[treeSize + i++], done: false };
|
|
260
556
|
}
|
|
261
|
-
|
|
262
|
-
if (cur.right) {
|
|
263
|
-
dfs(cur.right, index, sum, value);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
if (cur.left && cur.right) {
|
|
267
|
-
cur.sum = cur.left.sum + cur.right.sum;
|
|
557
|
+
return { value: undefined, done: true } as IteratorResult<E>;
|
|
268
558
|
}
|
|
269
559
|
};
|
|
270
|
-
|
|
271
|
-
dfs(root, index, sum, value);
|
|
272
560
|
}
|
|
273
561
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
* @param {number} indexB - The parameter `indexB` represents the ending index of the range for which you want to
|
|
278
|
-
* calculate the sum.
|
|
279
|
-
* @returns The function `querySumByRange` returns a number.
|
|
280
|
-
*/
|
|
281
|
-
querySumByRange(indexA: number, indexB: number): number {
|
|
282
|
-
const root = this.root || undefined;
|
|
283
|
-
if (!root) {
|
|
284
|
-
return 0;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (indexA < 0 || indexB >= this.values.length || indexA > indexB) {
|
|
288
|
-
return NaN;
|
|
562
|
+
forEach(callback: (value: E, index: number) => void): void {
|
|
563
|
+
for (let i = 0; i < this._n; i++) {
|
|
564
|
+
callback(this._tree[this._treeSize + i], i);
|
|
289
565
|
}
|
|
566
|
+
}
|
|
290
567
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
// The range [i, j] completely covers the current node's range [cur.start, cur.end]
|
|
294
|
-
return cur.sum;
|
|
295
|
-
}
|
|
296
|
-
const mid = cur.start + Math.floor((cur.end - cur.start) / 2);
|
|
297
|
-
if (j <= mid) {
|
|
298
|
-
if (cur.left) {
|
|
299
|
-
return dfs(cur.left, i, j);
|
|
300
|
-
} else {
|
|
301
|
-
return NaN;
|
|
302
|
-
}
|
|
303
|
-
} else if (i > mid) {
|
|
304
|
-
if (cur.right) {
|
|
305
|
-
return dfs(cur.right, i, j);
|
|
306
|
-
} else {
|
|
307
|
-
return NaN;
|
|
308
|
-
}
|
|
309
|
-
} else {
|
|
310
|
-
// Query both left and right subtrees
|
|
311
|
-
let leftSum = 0;
|
|
312
|
-
let rightSum = 0;
|
|
313
|
-
if (cur.left) {
|
|
314
|
-
leftSum = dfs(cur.left, i, mid);
|
|
315
|
-
}
|
|
316
|
-
if (cur.right) {
|
|
317
|
-
rightSum = dfs(cur.right, mid + 1, j);
|
|
318
|
-
}
|
|
319
|
-
return leftSum + rightSum;
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
return dfs(root, indexA, indexB);
|
|
568
|
+
print(): void {
|
|
569
|
+
console.log(this.toArray());
|
|
323
570
|
}
|
|
324
571
|
}
|
|
572
|
+
|
|
573
|
+
|