Segment Trees: Efficient Range Queries
A segment tree is a balanced binary tree over an array that answers range queries (sum, min, max, gcd) and point/range updates in time after construction. Key result: any associative operation can be aggregated over a contiguous range by combining precomputed canonical segments. Used in competitive programming, computational geometry, and database range aggregation. Folklore data structure formalized through the 1970s–1980s (Bentley, de Berg et al.).
A contiguous range over a power-of-two-aligned binary subdivision is the disjoint union of at most maximal aligned subintervals (canonical segments). Precompute the aggregate of every aligned subinterval once; then any query is the merge of a logarithmic number of these stored aggregates. The merge must be associative so the order of combination is irrelevant.
Formal Statement
Let be an array with values in a set equipped with an associative binary operation (a semigroup; a monoid if an identity exists). The segment tree is a binary tree where each node is labeled with an interval and a stored aggregate
Derivation hint: define the aggregate of a node as the fold of its interval; leaves have so . The root has interval . Each internal node with interval and midpoint has children covering and , giving the recurrence
Derivation hint: associativity lets the fold over split at into the fold of followed by that of .
In the array-based ("heap-indexed") implementation, node is the root, and node has children and . The array requires size at most :
Derivation hint: round up to the next power of two ; a complete binary tree with leaves has nodes.
For any query interval , the set of nodes selected by the standard recursive query forms a partition of into canonical segments, and the number of such nodes is at most . Hence a query costs
Derivation hint: at each tree depth the query frontier contains at most two "boundary" nodes that recurse further; all others are fully inside or fully outside and terminate.
Updating a single element requires recomputing along the root-to-leaf path through the leaf for index , a path of length , so point update costs .
Why It Holds
We bound the number of nodes visited by the recursive query `query(v, l, r)` which returns the aggregate of . 1. Three cases per node. At node with interval : (a) if , return identity and stop; (b) if , return and stop (a canonical segment); (c) otherwise recurse into both children and merge. 2. Counting boundary nodes. Case (c) occurs only when straddles a query endpoint, i.e. contains but not all of , or contains . Consider the path tracking : at each level the half containing may split further (case c), while the other half is either fully inside (case b, stops) or fully outside (case a, stops). So along the left boundary at most one node per level recurses; same for the right boundary. 3. Per-level bound. Hence at each of the levels () at most two nodes are in case (c), and each contributes at most two stopping children. The total visited nodes is .
∎The returned value of `query(v,l,r)` equals , by induction on subtree height. Base: a leaf or a fully-inside node returns its stored fold, correct by . Inductive step: a straddling node returns ; since the left and right index sets are disjoint and their union is , associativity (and, where used, commutativity is not required because the canonical segments are concatenated in left-to-right order) gives the full fold.
∎Associativity alone suffices only if you merge canonical segments left-to-right in index order. If your merge is not commutative (e.g. matrix multiplication, string concatenation, function composition), you must preserve order when combining the left-subtree result with the right-subtree result. Swapping them silently breaks correctness.
Building Intuition
Think of the array as a ruler marked at every position. A segment tree is the set of all aligned "rulers within rulers": the whole thing, its two halves, their four quarters, and so on. To measure any contiguous stretch, you don't measure unit by unit — you greedily snap to the largest pre-measured aligned chunk that fits, then handle the leftover at both ends. Because halving the interval reaches a single element in steps, you never need more than a couple of chunks per level. The tree "pre-pays" the cost of aggregation so that queries become cheap lookups plus a logarithmic stitching step.
:::svg[Segment tree over array A=[5,2,7,1] showing canonical segments for query sum over [1,3)]
Worked Examples
Given: , monoid .
Build: leaves ; internals , ; root .
Query : 1. Root straddles → recurse. 2. Left straddles → recurse; right straddles → recurse. 3. From left subtree: outside (drop), inside → take. 4. From right subtree: inside → take, outside (drop). 5. Merge in order: (canonical segments: ).
Update (was ):
- Walk leaf : set to .
- Recompute parent .
- Recompute root .
Re-query (units: pure integer sum).
Given: , semigroup .
Build (midpoints split unevenly for ): root , children and .
- : ; : ; root .
Query : 1. Root straddles → recurse. 2. straddles : taken; inside → take. Subresult . 3. straddles : splits → , both inside; outside. Subresult . 4. Merge: . Verify directly: . ✓ (Note is idempotent and commutative, so even overlapping segments would be safe — a property exploited by the sparse table variant.)
Common Misconceptions
Segment tree vs. Binary Indexed Tree (Fenwick). A Fenwick tree is smaller and faster in constant factor but supports a narrower class of operations (cleanly invertible, like sum/xor) and is awkward for or arbitrary monoids. The segment tree is strictly more general — at the cost of – memory and constant factor. Do not assume they are interchangeable for range-min-with-updates.
Suppose you try to maintain "the value of the second-largest distinct element" of each interval as the merge. This operation is not associative: knowing the top-2 of and the top-2 of does not determine the second-largest of from a single scalar — you must store enough state (here, the top-2 multiset). The lesson: if your query function is non-associative, you must enlarge the node label to a structure that is associative under a well-defined merge, or the tree gives wrong answers.
Lazy propagation is mandatory for fast range updates. A point-update tree handles "add to one element" in , but "add to all of " naively costs . Lazy propagation stores a pending update at each node and pushes it down only when needed, restoring . Forgetting to push down before recursing into children is the single most common implementation bug.
Connections
The segment tree generalizes the Prefix Sum Array (which gives range sum but update) by trading a factor on queries for cheap updates. It is the dynamic cousin of the Sparse Table (which gives idempotent range queries but no updates). Its merge structure is precisely a Monoid homomorphism over intervals, linking it to algebraic Semigroup theory. Geometrically, the interval tree and segment tree of Computational Geometry solve stabbing and windowing queries using the same canonical-decomposition principle. The lazy-propagation variant relates to Amortized Analysis and the potential method.
Real-World Applications
Historical Development
Subtleties & Limits
What is the true cost frontier? For semigroup range queries with updates, per operation is optimal in the comparison/algebraic model under reasonable assumptions, but constant-factor and cache-behavior optimization remains an engineering frontier (iterative bottom-up trees, SIMD-friendly layouts, van Emde Boas memory orderings). In the static idempotent case, sparse tables achieve query — and for general semigroups the disjoint sparse table also reaches query with build, showing the update-vs-query tradeoff is fundamental rather than incidental. The genuinely open territory lies in higher dimensions: -dimensional range queries with updates cost , and whether the poly-log exponent can be reduced for specific monoids (e.g. via fractional cascading or word-RAM tricks) is an active question. Persistence adds another axis: how cheaply can one support time-travel queries with bounded total memory?
See Also
Prefix Sum Array · Fenwick Tree · Sparse Table · Lazy Propagation · Monoid · Persistent Data Structures · Computational Geometry · Binary Trees