Segment Trees & BIT (Fenwick Tree) — Complete Guide
Advertisement
Why Range Query Structures?
- Naive: O(n) per query, O(1) update
- Prefix sums: O(1) query, O(n) update
- Segment Tree / BIT: O(log n) both query and update
Binary Indexed Tree (Fenwick Tree)
The simplest structure for prefix sum queries with point updates.
BIT Operations
class BIT:
def __init__(self, n):
self.n = n
self.tree = [0] * (n + 1)
def update(self, i, delta):
while i <= self.n:
self.tree[i] += delta
i += i & (-i) # move to parent
def query(self, i): # prefix sum [1..i]
s = 0
while i > 0:
s += self.tree[i]
i -= i & (-i) # move to left sibling
return s
def range_query(self, l, r):
return self.query(r) - self.query(l - 1)
BIT Trick: i & (-i) isolates lowest set bit
- Update: add to i, then jump to next by adding
i & (-i) - Query: sum from i, then jump to left by subtracting
i & (-i)
Segment Tree
More powerful: supports any associative operation (min, max, GCD, etc.) and range updates with lazy propagation.
Simple Segment Tree
class SegTree:
def __init__(self, n):
self.n = n
self.tree = [0] * (4 * n)
def build(self, arr, node, start, end):
if start == end:
self.tree[node] = arr[start]
else:
mid = (start + end) // 2
self.build(arr, 2*node, start, mid)
self.build(arr, 2*node+1, mid+1, end)
self.tree[node] = self.tree[2*node] + self.tree[2*node+1]
def update(self, node, start, end, idx, val):
if start == end:
self.tree[node] = val
else:
mid = (start + end) // 2
if idx <= mid:
self.update(2*node, start, mid, idx, val)
else:
self.update(2*node+1, mid+1, end, idx, val)
self.tree[node] = self.tree[2*node] + self.tree[2*node+1]
def query(self, node, start, end, l, r):
if r < start or end < l: return 0 # out of range
if l <= start and end <= r: return self.tree[node] # complete overlap
mid = (start + end) // 2
return self.query(2*node, start, mid, l, r) + self.query(2*node+1, mid+1, end, l, r)
Lazy Propagation (Range Updates)
# Mark pending update at node; push down before querying children
def push_down(self, node, start, end):
if self.lazy[node] != 0:
mid = (start + end) // 2
self.tree[2*node] += self.lazy[node] * (mid - start + 1)
self.tree[2*node+1] += self.lazy[node] * (end - mid)
self.lazy[2*node] += self.lazy[node]
self.lazy[2*node+1] += self.lazy[node]
self.lazy[node] = 0
Complexity
| Structure | Build | Point Update | Range Query | Range Update |
|---|---|---|---|---|
| BIT | O(n) | O(log n) | O(log n) | — |
| Segment Tree | O(n) | O(log n) | O(log n) | O(log n) lazy |
Problem Index
| # | Problem | Structure | Difficulty |
|---|---|---|---|
| 01 | Range Sum Query Mutable | BIT / Seg Tree | Medium |
| 02 | Count of Smaller Numbers After Self | BIT / Merge Sort | Hard |
| 03 | Count of Range Sum | Merge Sort / BIT | Hard |
| 04 | Queue Reconstruction (revisit) | BIT order | Medium |
| 05 | Reverse Pairs | BIT / Merge Sort | Hard |
| 06 | Range Sum Query 2D Mutable | 2D BIT | Hard |
| 07 | My Calendar I, II, III | Segment Tree / Sorted | Medium/Hard |
| 08 | Falling Squares | Coordinate compress + SegTree | Hard |
| 09 | Rectangle Area II | Coordinate compress | Hard |
| 10 | Number of Longest Increasing Subsequence | Segment Tree on LIS | Medium |
| 11 | Max Sum of Rectangle No Larger Than K | BIT + prefix | Hard |
| 12 | Longest Increasing Subsequence (Seg Tree) | SegTree on value | Medium |
| 13 | Shifting Letters II | Difference Array + BIT | Medium |
| 14 | Stamping the Grid | Prefix sums 2D | Hard |
| 15 | Segment Tree Master Recap | Cheatsheet | — |
Advertisement