What is a Segment Tree?
A Segment Tree answers a question that neither a plain array nor a running-total (prefix-sum) array handles well at the same time: "what's the sum (or min, max, gcd...) of elements from index l to r?", while also supporting fast updates to individual elements. A prefix-sum array answers range queries in O(1), but a single update forces you to recompute every prefix after it, O(n) per update. A segment tree gets both operations down to O(log n).
How Is It Built?
Every node in a segment tree represents a contiguous range of the underlying array. Leaves represent single elements; every internal node represents the union of its two children's ranges, and stores the combined result of some associative operation (sum, min, max, etc.) over that whole range, computed once and cached, not recomputed from scratch on every query.
How Does a Range Query Work?
A range query works by decomposing the query range into a small number of these pre-combined node ranges. Starting from the root, if a node's range falls entirely outside the query, it's skipped. If it falls entirely inside the query, its cached value is used directly, no need to look at its children at all. Only when a node's range partially overlaps the query does the search recurse into both children. This decomposition never needs more than O(log n) nodes to cover any range.
How Does a Point Update Work?
A point update walks straight from the root down to the one leaf that needs to change, updates it, and then recomputes each ancestor's cached value on the way back up, exactly one path of length O(log n), touching nothing else in the tree.
Algorithm Steps
- Build: recursively split [l, r] at its midpoint until reaching single-element leaves, then combine each pair of children's results going back up
- Range Query(ql, qr) on the current node's range [l, r]:
- If [l, r] is entirely outside [ql, qr], return the operation's identity (0 for sum) and stop
- If [l, r] is entirely inside [ql, qr], return this node's cached value directly
- Otherwise, recurse into both children and combine their results
- Point Update(index, value): descend to the leaf for that index, set its value, then recompute every ancestor's cached value on the way back up
Time Complexity
- Build: O(n), every node is computed exactly once.
- Range Query: O(log n), at most a constant number of nodes per level are visited.
- Point Update: O(log n), exactly one root-to-leaf path is touched.