Skip to content

Commit 0dfcbac

Browse files
authored
Much simpler range sum operation
I have provided the overview and implementation of a much simpler range sum query function, which does not require splitting the query. I have explicitly outlined the three cases that may arise while processing a range sum query and the code directly reflects these cases. This implementation is much more beginner friendly and intuitive.
1 parent d890b87 commit 0dfcbac

File tree

1 file changed

+12
-21
lines changed

1 file changed

+12
-21
lines changed

src/data_structures/segment_tree.md

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -84,22 +84,14 @@ For now we are going to answer sum queries. As an input we receive two integers
8484

8585
To do this, we will traverse the Segment Tree and use the precomputed sums of the segments.
8686
Let's assume that we are currently at the vertex that covers the segment $a[tl \dots tr]$.
87-
There are three possible cases.
87+
There are three possible cases:
88+
1. $[tl \dots tr]$ is completely contained in $[l \dots r]$ : In this case, we know that the sum of this segment will surely be a part of the final answer, so we return this sum.
89+
2. $[tl \dots tr]$ partially covers $[l \dots r]$ : In this case, we cannot include this vertex's sum and we must explore both the left and right children. First we go to the left child($[tl \dots tm]$), compute a partial answer for this vertex (i.e. the sum of values of the intersection between the segment of the query and the segment of the left child), then go to the right child($[tm + 1 \dots tr]$, compute the partial answer using that vertex, and then combine the answers by adding them.
90+
3. There is no intersection between $[tl \dots tr]$ and $[l \dots r]$ : This segment does not contribute to the sum, so we return zero.
8891

89-
The easiest case is when the segment $a[l \dots r]$ is equal to the corresponding segment of the current vertex (i.e. $a[l \dots r] = a[tl \dots tr]$), then we are finished and can return the precomputed sum that is stored in the vertex.
92+
So, processing a sum query is a function that recursively calls itself with left and right children until it finds a complete or no overlap between ranges.
9093

91-
Alternatively the segment of the query can fall completely into the domain of either the left or the right child.
92-
Recall that the left child covers the segment $a[tl \dots tm]$ and the right vertex covers the segment $a[tm + 1 \dots tr]$ with $tm = (tl + tr) / 2$.
93-
In this case we can simply go to the child vertex, which corresponding segment covers the query segment, and execute the algorithm described here with that vertex.
94-
95-
And then there is the last case, the query segment intersects with both children.
96-
In this case we have no other option as to make two recursive calls, one for each child.
97-
First we go to the left child, compute a partial answer for this vertex (i.e. the sum of values of the intersection between the segment of the query and the segment of the left child), then go to the right child, compute the partial answer using that vertex, and then combine the answers by adding them.
98-
In other words, since the left child represents the segment $a[tl \dots tm]$ and the right child the segment $a[tm+1 \dots tr]$, we compute the sum query $a[l \dots tm]$ using the left child, and the sum query $a[tm+1 \dots r]$ using the right child.
99-
100-
So processing a sum query is a function that recursively calls itself once with either the left or the right child (without changing the query boundaries), or twice, once for the left and once for the right child (by splitting the query into two subqueries).
101-
And the recursion ends, whenever the boundaries of the current query segment coincides with the boundaries of the segment of the current vertex.
102-
In that case the answer will be the precomputed value of the sum of this segment, which is stored in the tree.
94+
So processing a sum query is a function that recursively calls itself once with either the left or the right child (without changing the query boundaries), or twice, once for the left and once for the right child (by splitting the query into two subqueries). And the recursion ends, whenever the boundaries of the current query segment coincides with the boundaries of the segment of the current vertex. In that case the answer will be the precomputed value of the sum of this segment, which is stored in the tree.
10395

10496
In other words, the calculation of the query is a traversal of the tree, which spreads through all necessary branches of the tree, and uses the precomputed sum values of the segments in the tree.
10597

@@ -202,14 +194,13 @@ In order to simplify the code, this function always does two recursive calls, ev
202194
203195
```{.cpp file=segment_tree_implementation_sum}
204196
int sum(int v, int tl, int tr, int l, int r) {
205-
if (l > r)
206-
return 0;
207-
if (l == tl && r == tr) {
208-
return t[v];
209-
}
197+
if (tr < l || tl > r) return 0; // no overlap
198+
if (tl >= l && tr <= r) return t[v]; // complete overlap
199+
210200
int tm = (tl + tr) / 2;
211-
return sum(v*2, tl, tm, l, min(r, tm))
212-
+ sum(v*2+1, tm+1, tr, max(l, tm+1), r);
201+
// partial overlap
202+
return sum(v * 2, tl, tm, l, r)
203+
+ sum(v * 2 + 1, tm + 1, tr, l, r);
213204
}
214205
```
215206

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy