@@ -12,10 +12,10 @@ export class SegmentTree {
12
12
/**
13
13
* Operation function
14
14
*
15
- * @type {function }
15
+ * @type {function<*> }
16
16
* @private
17
17
*/
18
- private operation : ( ) => { } ;
18
+ private operation : ( ... args ) => number ;
19
19
20
20
/**
21
21
* Fallback value for non-intersect intervals
@@ -37,30 +37,177 @@ export class SegmentTree {
37
37
* @param {function } operation
38
38
* @param {number } fallbackValue
39
39
*/
40
- constructor ( input : number [ ] , operation : ( ) => { } , fallbackValue : number = 0 ) {
40
+ constructor (
41
+ input : number [ ] ,
42
+ operation : ( ...args ) => number ,
43
+ fallbackValue : number = 0
44
+ ) {
41
45
this . input = input ;
42
46
this . operation = operation ;
43
47
this . fallbackValue = fallbackValue ;
44
48
45
49
this . tree = this . createTree ( ) ;
50
+
51
+ this . buildTree ( ) ;
52
+ }
53
+
54
+ /**
55
+ * Query on segment tree in context of this.operation function
56
+ *
57
+ * @param {number } queryLeftIndex
58
+ * @param {number } queryRightIndex
59
+ * @return {number }
60
+ */
61
+ public query ( queryLeftIndex , queryRightIndex ) : number {
62
+ const leftIndex = 0 ;
63
+ const rightIndex = this . input . length - 1 ;
64
+
65
+ return this . queryRecursive (
66
+ queryLeftIndex ,
67
+ queryRightIndex ,
68
+ leftIndex ,
69
+ rightIndex
70
+ ) ;
46
71
}
47
72
73
+ /**
74
+ * @returns {number[] }
75
+ */
48
76
private createTree ( ) : number [ ] {
49
- let segmentTreeArrayLength ;
50
- const inputArrayLength = this . input . length ;
77
+ let arrayLength = this . input . length ;
51
78
52
- let treeArrayLength = inputArrayLength ;
79
+ if ( ! arrayLength ) {
80
+ return [ ] ;
81
+ }
53
82
54
- if ( ! isPowerOfTwo ( inputArrayLength ) ) {
83
+ if ( arrayLength && ! isPowerOfTwo ( arrayLength ) ) {
55
84
// If original array length is not a power of two then we need to find
56
85
// next number that is a power of two and use it to calculate
57
86
// tree array size. This is happens because we need to fill empty children
58
87
// in perfect binary tree with nulls.And those nulls need extra space.
59
- treeArrayLength = nearestHighestPowerOfTwoResult ( inputArrayLength + 1 ) ;
88
+ arrayLength = nearestHighestPowerOfTwoResult ( arrayLength + 1 ) ;
89
+ }
90
+
91
+ return new Array ( 2 * arrayLength - 1 ) . fill ( null ) ;
92
+ }
93
+
94
+ /**
95
+ * Build segment tree.
96
+ */
97
+ private buildTree ( ) : void {
98
+ const leftIndex = 0 ;
99
+ const rightIndex = this . input . length - 1 ;
100
+
101
+ this . buildTreeRecursively ( leftIndex , rightIndex ) ;
102
+ }
103
+
104
+ /**
105
+ * Build segment tree recursively.
106
+ *
107
+ * @param {number } leftIndex
108
+ * @param {number } rightIndex
109
+ * @param {number } position
110
+ */
111
+ private buildTreeRecursively ( leftIndex , rightIndex , position = 0 ) : void {
112
+ // If low input index and high input index are equal that would mean
113
+ // the we have finished splitting and we are already came to the leaf
114
+ // of the segment tree. We need to copy this leaf value from input
115
+ // array to segment tree.
116
+ if ( leftIndex === rightIndex ) {
117
+ return void ( this . tree [ position ] = this . input [ leftIndex ] ) ;
60
118
}
61
119
62
- segmentTreeArrayLength = 2 * treeArrayLength - 1 ;
120
+ // Split input array on two halves and process them recursively.
121
+ const middleIndex = ( leftIndex + rightIndex ) >> 1 ;
122
+
123
+ // Process left half of the input array.
124
+ this . buildTreeRecursively (
125
+ leftIndex ,
126
+ middleIndex ,
127
+ this . computeLeftChildIndex ( position )
128
+ ) ;
63
129
64
- return new Array ( segmentTreeArrayLength ) . fill ( null ) ;
130
+ // Process right half of the input array.
131
+ this . buildTreeRecursively (
132
+ middleIndex + 1 ,
133
+ rightIndex ,
134
+ this . computeRightChildIndex ( position )
135
+ ) ;
136
+
137
+ // Once every tree leaf is not empty we're able to build tree bottom up using
138
+ // provided operation function.
139
+ this . tree [ position ] = this . operation (
140
+ this . tree [ this . computeLeftChildIndex ( position ) ] ,
141
+ this . tree [ this . computeRightChildIndex ( position ) ]
142
+ ) ;
143
+ }
144
+
145
+ /**
146
+ * Query on segment tree recursively in context of this.operation function
147
+ *
148
+ * @param {number } queryLeftIndex left index of the query
149
+ * @param {number } queryRightIndex right index of the query
150
+ * @param {number } leftIndex left index of input array segment
151
+ * @param {number } rightIndex right index of input array segment
152
+ * @param {number } position root position in binary tree
153
+ * @return {number }
154
+ */
155
+ private queryRecursive (
156
+ queryLeftIndex ,
157
+ queryRightIndex ,
158
+ leftIndex ,
159
+ rightIndex ,
160
+ position = 0
161
+ ) : number {
162
+ if ( queryLeftIndex <= leftIndex && queryRightIndex >= rightIndex ) {
163
+ // Total overlap
164
+ return this . tree [ position ] ;
165
+ }
166
+
167
+ if ( queryLeftIndex > rightIndex || queryRightIndex < leftIndex ) {
168
+ // No overlap. Return fallback value
169
+ return this . fallbackValue ;
170
+ }
171
+
172
+ // Partial overlap
173
+ const middleIndex = ( leftIndex + rightIndex ) >> 1 ;
174
+
175
+ const leftOperationResult = this . queryRecursive (
176
+ queryLeftIndex ,
177
+ queryRightIndex ,
178
+ leftIndex ,
179
+ middleIndex ,
180
+ this . computeLeftChildIndex ( position )
181
+ ) ;
182
+
183
+ const rightOperationResult = this . queryRecursive (
184
+ queryLeftIndex ,
185
+ queryRightIndex ,
186
+ middleIndex + 1 ,
187
+ rightIndex ,
188
+ this . computeRightChildIndex ( position )
189
+ ) ;
190
+
191
+ return this . operation ( leftOperationResult , rightOperationResult ) ;
192
+ }
193
+
194
+ /**
195
+ * Computes left child index
196
+ *
197
+ * @param {number } position
198
+ * @returns {number }
199
+ */
200
+ private computeLeftChildIndex ( position : number ) : number {
201
+ return ( position << 1 ) + 1 ;
202
+ }
203
+
204
+ /**
205
+ * Computes right child index
206
+ *
207
+ * @param {number } position
208
+ * @returns {number }
209
+ */
210
+ private computeRightChildIndex ( position : number ) : number {
211
+ return ( position << 1 ) + 2 ;
65
212
}
66
213
}
0 commit comments