Segment Trees and Fenwick Trees

Range queries with point updates: building a segment tree, querying by overlap cases, updates, lazy propagation, the Fenwick (binary indexed) tree with i & -i, and comparing them with prefix sums.

Prefix sums answer range-sum queries in O(1), but a single update can cost O(n), because many prefix values change. When an array is both queried and updated often, two tree structures give O(log n) for both.

StructureBuildRange queryPoint updateSpace
Plain arrayO(1)O(n)O(1)n
Prefix sumsO(n)O(1)O(n)n
Segment treeO(n)O(log n)O(log n)about 4n
Fenwick treeO(n log n)O(log n)O(log n)n + 1

Segment tree

A segment tree is a binary tree in which each node stores the answer (sum, minimum, maximum, gcd…) for a range of the array. The root covers a[0..n−1]; each node's children cover the two halves of its range; leaves are single elements.

21a[0..5]13a[0..2]7a[0..1]5a[0]2a[1]6a[2]8a[3..5]4a[3..4]1a[3]3a[4]4a[5]gateshala.com
Segment tree of sums for a = [5, 2, 6, 1, 3, 4]

Stored in an array: node i has children 2i and 2i + 1, and an array of size 4n is always enough.

Query a[l..r] at a node covering [lo, hi]:

  1. No overlap (r < lo or hi < l): return 0.
  2. Full overlap (l ≤ lo and hi ≤ r): return the stored value.
  3. Partial overlap: ask both children and combine.

At most about 4 nodes per level are visited, so a query is O(log n). An update changes one leaf and recomputes the O(log n) nodes on its path to the root.

Lazy propagation handles range updates (add v to every element of a[l..r]) in O(log n) by storing pending updates at nodes and pushing them down only when needed.

Fenwick tree (binary indexed tree)

A shorter, faster structure for prefix sums (and any invertible operation). With 1-based index i, bit[i] stores the sum of the last (i & −i) elements ending at i.

  • Prefix sum(i): add bit[i], then i −= i & −i, until i = 0.
  • Add v at i: bit[i] += v, then i += i & −i, until i > n.
  • Range sum(l, r) = prefix(r) − prefix(l − 1).

Each loop removes or adds the lowest set bit, so it runs at most log₂ n times. See Bit Manipulation for why i & −i is the lowest set bit.

The code

public class Main {
    static int n;
    static int[] tree;                               // segment tree for range sums

    static void build(int[] a, int node, int lo, int hi) {
        if (lo == hi) { tree[node] = a[lo]; return; }
        int mid = (lo + hi) / 2;
        build(a, 2 * node, lo, mid);
        build(a, 2 * node + 1, mid + 1, hi);
        tree[node] = tree[2 * node] + tree[2 * node + 1];
    }

    static int query(int node, int lo, int hi, int l, int r) {
        if (r < lo || hi < l) return 0;              // no overlap
        if (l <= lo && hi <= r) return tree[node];   // full overlap
        int mid = (lo + hi) / 2;                     // partial overlap: ask both halves
        return query(2 * node, lo, mid, l, r) + query(2 * node + 1, mid + 1, hi, l, r);
    }

    static void update(int node, int lo, int hi, int i, int value) {
        if (lo == hi) { tree[node] = value; return; }
        int mid = (lo + hi) / 2;
        if (i <= mid) update(2 * node, lo, mid, i, value);
        else update(2 * node + 1, mid + 1, hi, i, value);
        tree[node] = tree[2 * node] + tree[2 * node + 1];
    }

    // Fenwick (binary indexed) tree: bit[i] covers the last (i & -i) positions up to i.
    static int[] bit;
    static void add(int i, int delta) { for (i++; i <= n; i += i & -i) bit[i] += delta; }
    static int prefix(int i) { int s = 0; for (i++; i > 0; i -= i & -i) s += bit[i]; return s; }

    public static void main(String[] args) {
        int[] a = {5, 2, 6, 1, 3, 4};
        n = a.length;
        tree = new int[4 * n];
        build(a, 1, 0, n - 1);
        System.out.println("segment tree: sum a[1..4] = " + query(1, 0, n - 1, 1, 4));
        update(1, 0, n - 1, 3, 10);                  // a[3] = 10
        System.out.println("after a[3] = 10: sum a[1..4] = " + query(1, 0, n - 1, 1, 4));

        bit = new int[n + 1];
        for (int i = 0; i < n; i++) add(i, a[i]);
        System.out.println("Fenwick: sum a[1..4] = " + (prefix(4) - prefix(0)));
        add(3, 10 - a[3]);
        System.out.println("after a[3] = 10: sum a[1..4] = " + (prefix(4) - prefix(0)));
    }
}
Output
segment tree: sum a[1..4] = 12
after a[3] = 10: sum a[1..4] = 21
Fenwick: sum a[1..4] = 12
after a[3] = 10: sum a[1..4] = 21

GATE-style practice questions

GATE-styleNAT · numerical answer

A segment tree is built over an array of 8 elements (n a power of 2). How many nodes does it have?

Show answer and explanationAnswer: 15

A full binary tree with 8 leaves has 2n − 1 = 15 nodes.

GATE-styleNAT · numerical answer

In a Fenwick tree (1-based), how many elements does bit[12] cover?

Show answer and explanationAnswer: 4

12 = 1100₂; its lowest set bit is 4, so bit[12] holds the sum of positions 9 to 12.

GATE-styleMCQ · one correct answer

Which indices does a Fenwick prefix-sum query for i = 13 visit (1-based)?

Show answer and explanationAnswer: A

Remove the lowest set bit each time: 13 (1101) → 12 (1100) → 8 (1000) → 0.

GATE-styleMCQ · one correct answer

An array receives many range-sum queries and many point updates, mixed together. Which structure gives O(log n) for both?

Show answer and explanationAnswer: B

Prefix sums make each update O(n); segment and Fenwick trees keep both operations logarithmic.