Skip to content

🌳 Binary Indexed Tree (Fenwick Tree) & Segment Tree Tutorial

Introduction

Efficiently answering range queries and updates is a common problem in computer science. Two powerful data structures for this are the Binary Indexed Tree (Fenwick Tree) and the Segment Tree. This tutorial introduces both, explains their use cases, and provides C++ code examples.


1. Binary Indexed Tree (Fenwick Tree)

What is it?

A Binary Indexed Tree (BIT), or Fenwick Tree, is a data structure that efficiently supports: - Point updates (add a value to a single element) - Prefix sum queries (sum of the first k elements)

Both operations run in O(log n) time.

Use Cases

  • Cumulative frequency tables
  • Range sum queries with point updates

Basic Operations

1.1. Initialization

std::vector<int> bit(n + 1, 0); // 1-based indexing

1.2. Update (add value to index)

void update(int idx, int val, std::vector<int>& bit) {
    while (idx < bit.size()) {
        bit[idx] += val;
        idx += idx & -idx;
    }
}

1.3. Query (prefix sum up to index)

int query(int idx, const std::vector<int>& bit) {
    int res = 0;
    while (idx > 0) {
        res += bit[idx];
        idx -= idx & -idx;
    }
    return res;
}

1.4. Example Usage

int n = 10;
std::vector<int> bit(n + 1, 0);
update(3, 5, bit); // add 5 to index 3
int sum = query(3, bit); // sum of elements 1..3

2. Segment Tree

What is it?

A Segment Tree is a binary tree data structure that supports: - Range queries (sum, min, max, etc. over a range) - Point or range updates

Both operations run in O(log n) time.

Use Cases

  • Range sum/min/max queries
  • Range updates (with lazy propagation)

Basic Operations

2.1. Initialization

std::vector<int> seg(4 * n, 0); // 1-based or 0-based

2.2. Build

void build(const std::vector<int>& arr, std::vector<int>& seg, int node, int l, int r) {
    if (l == r) {
        seg[node] = arr[l];
        return;
    }
    int mid = (l + r) / 2;
    build(arr, seg, 2 * node, l, mid);
    build(arr, seg, 2 * node + 1, mid + 1, r);
    seg[node] = seg[2 * node] + seg[2 * node + 1];
}

2.3. Query (sum over [ql, qr])

int query(const std::vector<int>& seg, 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 seg[node]; // total overlap
    int mid = (l + r) / 2;
    return query(seg, 2 * node, l, mid, ql, qr) +
           query(seg, 2 * node + 1, mid + 1, r, ql, qr);
}

2.4. Update (add value to index)

void update(std::vector<int>& seg, int node, int l, int r, int idx, int val) {
    if (l == r) {
        seg[node] += val;
        return;
    }
    int mid = (l + r) / 2;
    if (idx <= mid) update(seg, 2 * node, l, mid, idx, val);
    else update(seg, 2 * node + 1, mid + 1, r, idx, val);
    seg[node] = seg[2 * node] + seg[2 * node + 1];
}

2.5. Example Usage

int n = 10;
std::vector<int> arr(n + 1, 0), seg(4 * n, 0);
build(arr, seg, 1, 1, n);
update(seg, 1, 1, n, 3, 5); // add 5 to index 3
int sum = query(seg, 1, 1, n, 1, 3); // sum of elements 1..3

3. Comparison

Feature Binary Indexed Tree Segment Tree
Code Complexity Simple More complex
Memory Usage O(n) O(n) or O(4n)
Range Query Types Prefix sum Any (sum, min, max)
Range Updates No (basic) Yes (with lazy)
Point Updates Yes Yes
Typical Use Case Prefix sums General range query

4. Summary

  • Use BIT for simple prefix sums and point updates.
  • Use Segment Tree for more complex range queries and updates.

Both are essential tools for competitive programming and efficient algorithm design!