1. Home
  2. Data Structures
  3. Segment Tree

Segment Tree

Answer "what is the sum of a[l..r]?" in O(log n) — even while the array keeps changing. Build, query and update it in 3D.

Interactive 3DAdvanced14 min readDSAUpdated

Drag to rotate · Right-drag to pan · Click, then scroll to zoom · Space play · ←→ step

What's happening

Pseudocode

    Try this in the 3D model

    • Press Build and check that every parent equals the sum of its two children.
    • Query l = 2, r = 6. Count the green nodes — those are the only stored sums we needed.
    • Update index 3 to 10 and watch only one node per level change.
    • Query the whole array (l = 0, r = 7). How many nodes are visited?

    The problem

    You have an array of numbers and two kinds of requests arriving thousands of times:

    1. Query: what is the sum (or min, or max) of a[l..r]?
    2. Update: change a[i] to a new value.
    Approach Query Update
    Plain array O(n) — add up the range O(1)
    Prefix-sum array O(1) O(n) — rebuild prefixes
    Segment tree O(log n) O(log n)

    When both operations are frequent, the segment tree wins.

    The structure

    A segment tree is a binary tree where:

    • each leaf holds one array element,
    • each internal node covers a segment [l..r] and stores the sum of that segment,
    • a node covering [l..r] has children covering [l..mid] and [mid+1..r].

    The root covers the whole array. In the model, each node shows its sum, and the tag below it shows its range.

    It’s stored in an array of size about 2n (or 4n for safety): node k has children 2k and 2k + 1 — the same trick as a binary heap.

    Range query: three cases

    Starting at the root, for each node compare its range [l..r] with the query [ql..qr]:

    1. No overlap (completely outside) → return 0 (grey).
    2. Total overlap (completely inside) → return the stored sum, don’t go deeper (green).
    3. Partial overlap → ask both children and add the answers (yellow).

    Because only O(1) nodes per level are partially overlapping, a query visits O(log n) nodes.

    Point update

    Change the leaf, then walk up to the root recomputing node = left + right. That’s one node per level: O(log n).

    Code

    class SegmentTree:
        def __init__(self, a):
            self.n = len(a)
            self.t = [0] * (4 * self.n)
            self._build(a, 1, 0, self.n - 1)
    
        def _build(self, a, node, l, r):
            if l == r:
                self.t[node] = a[l]
                return
            mid = (l + r) // 2
            self._build(a, 2 * node, l, mid)
            self._build(a, 2 * node + 1, mid + 1, r)
            self.t[node] = self.t[2 * node] + self.t[2 * node + 1]
    
        def query(self, ql, qr, node=1, l=0, r=None):
            if r is None:
                r = self.n - 1
            if qr < l or r < ql:            # no overlap
                return 0
            if ql <= l and r <= qr:          # total overlap
                return self.t[node]
            mid = (l + r) // 2               # partial overlap
            return (self.query(ql, qr, 2 * node, l, mid) +
                    self.query(ql, qr, 2 * node + 1, mid + 1, r))
    
        def update(self, i, value, node=1, l=0, r=None):
            if r is None:
                r = self.n - 1
            if l == r:
                self.t[node] = value
                return
            mid = (l + r) // 2
            if i <= mid:
                self.update(i, value, 2 * node, l, mid)
            else:
                self.update(i, value, 2 * node + 1, mid + 1, r)
            self.t[node] = self.t[2 * node] + self.t[2 * node + 1]
    
    st = SegmentTree([5, 3, 8, 6, 1, 4, 7, 2])
    print(st.query(2, 6))   # 8+6+1+4+7 = 26
    st.update(3, 10)
    print(st.query(2, 6))   # 30
    #include <iostream>
    #include <vector>
    using namespace std;
    
    int n;
    vector<long long> t;
    
    void build(const vector<int>& a, int node, int l, int r) {
        if (l == r) { t[node] = a[l]; return; }
        int mid = (l + r) / 2;
        build(a, 2 * node, l, mid);
        build(a, 2 * node + 1, mid + 1, r);
        t[node] = t[2 * node] + t[2 * node + 1];
    }
    
    long long query(int node, int l, int r, int ql, int qr) {
        if (qr < l || r < ql) return 0;               // no overlap
        if (ql <= l && r <= qr) return t[node];       // total overlap
        int mid = (l + r) / 2;
        return query(2 * node, l, mid, ql, qr) + query(2 * node + 1, mid + 1, r, ql, qr);
    }
    
    void update(int node, int l, int r, int i, int v) {
        if (l == r) { t[node] = v; return; }
        int mid = (l + r) / 2;
        if (i <= mid) update(2 * node, l, mid, i, v);
        else update(2 * node + 1, mid + 1, r, i, v);
        t[node] = t[2 * node] + t[2 * node + 1];
    }
    
    int main() {
        vector<int> a = {5, 3, 8, 6, 1, 4, 7, 2};
        n = a.size();
        t.assign(4 * n, 0);
        build(a, 1, 0, n - 1);
        cout << query(1, 0, n - 1, 2, 6) << "\n";   // 26
    }

    Variations

    • Min / max / GCD segment trees: replace + with min, max or gcd.
    • Lazy propagation: update a whole range [l..r] in O(log n) by postponing work.
    • Fenwick tree (Binary Indexed Tree): a simpler, smaller structure for prefix sums with updates.

    Where is it used?

    Competitive programming, stock-price analytics (max over a time window), computational geometry, and games that need fast range statistics on changing data.

    Common mistakes

    • Allocating only 2n slots with recursive 1-based indexing — use 4n to be safe.
    • Mixing up inclusive and exclusive range ends.
    • Forgetting to recompute the parent after updating a child.

    Complexity at a glance

    Case / operationTimeWhy
    BuildO(n)Each of the ~2n nodes is computed once.
    Range queryO(log n)At most ~4 nodes per level are visited.
    Point updateO(log n)One node per level, leaf to root.
    Naive array (for comparison)O(n) query / O(1) updatePrefix sums are the opposite trade-off.
    Extra spaceO(n)

    Quick check

    Test yourself — pick an answer to see if you got it.

    1. What does each internal node of a sum segment tree store?

    2. During a query, a node's range is completely inside the query range. What happens?

    3. How many nodes change when you update one element of an array of size 8?

    4. Prefix sums answer range sums in O(1). Why use a segment tree instead?

    Saved only in this browser — no account needed.
    Spotted a mistake or a bug in the 3D model?

    Report a mistake

    in Segment Tree. Thank you — every report makes the lesson better for the next reader.

    We'll also include a link to the step of the 3D model you're on and your browser type, so we can reproduce it.