Segment Tree Pattern
A Segment Tree is a balanced binary tree built over an array where each internal node summarizes a contiguous range of the underlying data — typically a sum, min, max, gcd, or any associative function. It replaces O(n) range queries and O(n) updates with O(log n) for both, which is the standard answer whenever an interviewer combines range-aggregate queries with point updates on the same input.
By Sam K., Founder, InterviewChamp.AI · Last verified
Complexity
- Time
- O(log n) per query, O(log n) per update, O(n) build
- Space
- O(4n)
When to use Segment Tree
- The problem mixes range queries (sum, min, max over a subarray) with point updates that change individual elements.
- A naive prefix-sum solution would re-compute the entire prefix array on every update, giving O(n) per update.
- The aggregate operation is associative — sum, min, max, gcd, xor — so partial results from child nodes can be combined into a parent result.
- You need to answer many queries (thousands or more) against an array that also mutates many times.
- The input size is in the 10^4 to 10^5 range where O(n) per query is too slow but O(log n) is comfortable.
Template
class SegmentTree {
constructor(nums) {
this.n = nums.length;
this.tree = new Array(4 * this.n).fill(0);
if (this.n > 0) this.build(nums, 1, 0, this.n - 1);
}
build(nums, node, start, end) {
if (start === end) {
this.tree[node] = nums[start];
return;
}
const mid = (start + end) >> 1;
this.build(nums, 2 * node, start, mid);
this.build(nums, 2 * node + 1, mid + 1, end);
this.tree[node] = this.tree[2 * node] + this.tree[2 * node + 1];
}
update(index, value) {
this._update(1, 0, this.n - 1, index, value);
}
_update(node, start, end, index, value) {
if (start === end) {
this.tree[node] = value;
return;
}
const mid = (start + end) >> 1;
if (index <= mid) this._update(2 * node, start, mid, index, value);
else this._update(2 * node + 1, mid + 1, end, index, value);
this.tree[node] = this.tree[2 * node] + this.tree[2 * node + 1];
}
query(left, right) {
return this._query(1, 0, this.n - 1, left, right);
}
_query(node, start, end, left, right) {
if (right < start || end < left) return 0;
if (left <= start && end <= right) return this.tree[node];
const mid = (start + end) >> 1;
return this._query(2 * node, start, mid, left, right) + this._query(2 * node + 1, mid + 1, end, left, right);
}
}Example LeetCode problems
| # | Problem | Difficulty | Frequency |
|---|---|---|---|
| 307 | Range Sum Query - Mutable | medium | foundational |
| 315 | Count of Smaller Numbers After Self | hard | company favorite |
| 308 | Range Sum Query 2D - Mutable | hard | less common |
| 218 | The Skyline Problem | hard | classic |
| 699 | Falling Squares | hard | less common |
| 732 | My Calendar III | hard | frequently asked |
| 673 | Number of Longest Increasing Subsequence | medium | frequently asked |
| 2407 | Longest Increasing Subsequence II | hard | company favorite |
Common mistakes
- Off-by-one in the build recursion that puts a leaf into the wrong array slot, silently corrupting every query that touches it.
- Allocating only 2n slots instead of 4n — the tree is balanced but not perfectly so, and the last level needs the extra headroom.
- Forgetting to propagate the update up the tree after writing the leaf, leaving stale aggregates at every ancestor.
- Using inclusive vs exclusive range conventions inconsistently between query and update, producing answers that are one element short or one element too long.
- Picking a non-associative aggregate (like average or median) and getting wrong answers because parent nodes cannot be combined from child summaries.
Frequently asked questions
When should I use a Segment Tree instead of a Fenwick Tree?
Use a Segment Tree when the aggregate is min, max, gcd, or anything non-invertible, or when you need range updates with lazy propagation. Use a Fenwick Tree when the aggregate is sum or xor and the code-golf footprint matters — Fenwick is shorter, faster by a constant factor, and uses half the memory.
What is lazy propagation and when do I need it?
Lazy propagation defers a range update at an internal node until a query forces the children to be inspected. You need it whenever the problem combines range updates (add 5 to every element in [l, r]) with range queries; without it, the range update degrades to O(n log n).
Why do I need 4n nodes for a segment tree?
The tree is a perfect binary tree padded out to the next power of two, and the worst case has 2 * next_power_of_two(n) leaves. 4n is the cleanest safe bound that always fits, even for awkward sizes like n=5 or n=1000003.
Can a Segment Tree handle queries that ask for the kth element in a range?
Yes, with a merge-sort tree or a wavelet tree variant — each node stores a sorted list of its range instead of a single aggregate. The query then descends with a binary search at each level, giving O(log^2 n) per query.
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.