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 segment tree stores aggregate information about every interval of an array in a hierarchical fashion: the root covers , and each internal node splits its interval into two halves stored in its children, down to leaves covering single elements. Because any query interval decomposes into of these canonical node-intervals, queries and updates run in logarithmic time. This matters because naive range queries cost per query while a precomputed prefix-sum array cannot handle updates efficiently — the segment tree handles both in . The surprising fact: the same skeleton supports radically different problems (range minimum, range sum, range gcd, range assignment with lazy propagation, even geometric "stabbing" queries) simply by changing the merge operation, as long as it is associative.
The Core Insight
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
Segment Tree
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
The root has interval . Each internal node with interval and midpoint has children covering and , giving the recurrence
In the array-based ("heap-indexed") implementation, node is the root, and node has children and . The array requires size at most :
Query Decomposition
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
Point Update
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
Query runs in $O(\log n)$
We bound the number of nodes visited by the recursive query query(v, l, r) which returns the aggregate of .
- 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.
- 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.
- 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 .
Correctness via associativity
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
Range Sum Query with a Point Update
Given: , monoid . Build: leaves ; internals , ; root . Query :- Root straddles → recurse.
- Left straddles → recurse; right straddles → recurse.
- From left subtree: outside (drop), inside → take.
- From right subtree: inside → take, outside (drop).
- Merge in order: (canonical segments: ).
- Walk leaf : set to .
- Recompute parent .
- Recompute root .
Re-query (units: pure integer sum).
Range Minimum Query
Given: , semigroup . Build (midpoints split unevenly for ): root , children and .- : ; : ; root .
- Root straddles → recurse.
- straddles : taken; inside → take. Subresult .
- straddles : splits → , both inside; outside. Subresult .
- 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.Non-associative merge fails
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
- Competitive programming / algorithm contests — range sum/min/max/gcd with updates is a staple (Codeforces, ICPC).
- Database range aggregation — OLAP-style "sum of sales in a date range" with frequent inserts maps to lazy segment trees over time-indexed arrays.
- Computational geometry (windowing queries) — segment trees over -coordinates answer "which rectangles contain a vertical query line," used in CAD and VLSI design-rule checks.
- Genomics / sequence analysis — range GCD or range maximum over coverage arrays to find peaks in read-depth signals.
- Network traffic monitoring — sliding-window maxima of packet rates implemented via segment trees or their monotonic-queue cousins.
- Image processing — 2D segment trees (or Fenwick) for sub-rectangle sums in integral-image-with-updates pipelines.
- Financial time series — range-min/max over price windows for drawdown and high/low band computation with streaming updates.
Historical Development
- 1972 — Range query problems formalized within early data-structure research; prefix sums and partial-sum tables already standard.
- 1977 — Jon Bentley introduces the divide-and-conquer "tournament"/segment ideas; the maximum-subarray and range-aggregate framing emerge.
- 1980 — Segment trees appear explicitly in computational geometry for interval stabbing and the measure problem (Klee's measure problem solutions).
- 1985 — Fenwick-style implicit cumulative structures and balanced interval trees mature; Bentley & Ottmann sweep-line algorithms use segment-tree-like structures.
- 1994 — Peter Fenwick publishes the Binary Indexed Tree, the lightweight specialization for cumulative frequency.
- 1997 — de Berg, van Kreveld, Overmars, Schwarzkopf codify segment trees and interval trees in Computational Geometry: Algorithms and Applications, the canonical textbook treatment.
- 2000s — Lazy propagation and "merge sort tree" / persistent segment tree variants become widely taught through competitive programming communities.
- 2010s — Persistent and iterative (bottom-up) segment trees, plus the "Li Chao tree" for convex-hull-trick queries, become standard repertoire.