Segment Trees and BIT — Master Recap and Interview Cheatsheet
Advertisement
Segment Trees and BIT Master Cheatsheet
Complete quick-reference for every range query template covered in this series.
BIT (Fenwick Tree) Template
class BIT:
def __init__(self, n):
self.n = n
self.tree = [0] * (n + 1) # always 1-indexed
def update(self, i, delta):
while i <= self.n:
self.tree[i] += delta
i += i & (-i) # move to next responsible ancestor
def query(self, i):
# Returns prefix sum [1..i]
s = 0
while i > 0:
s += self.tree[i]
i -= i & (-i) # move to covered left boundary
return s
def range_query(self, l, r):
return self.query(r) - self.query(l - 1)Segment Tree Template
class SegTree:
def __init__(self, n):
self.tree = [0] * (4 * n) # 4*n handles all array sizes safely
self.n = n
def update(self, node, start, end, idx, val):
if start == end:
self.tree[node] = val
return
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: identity element for sum
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 Template (Range Updates)
class LazySegTree:
def __init__(self, n):
self.n = n
self.tree = [0] * (4 * n)
self.lazy = [0] * (4 * n)
def _push_down(self, node, start, end):
if self.lazy[node]:
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
def range_update(self, node, start, end, l, r, val):
if r < start or end < l:
return
if l <= start and end <= r:
self.tree[node] += val * (end - start + 1)
self.lazy[node] += val
return
self._push_down(node, start, end)
mid = (start + end) // 2
self.range_update(2*node, start, mid, l, r, val)
self.range_update(2*node+1, mid+1, end, l, r, val)
self.tree[node] = self.tree[2*node] + self.tree[2*node+1]2D BIT Template
class BIT2D:
def __init__(self, m, n):
self.m, self.n = m, n
self.tree = [[0] * (n + 1) for _ in range(m + 1)]
def update(self, r, c, delta):
i = r
while i <= self.m:
j = c
while j <= self.n:
self.tree[i][j] += delta
j += j & (-j)
i += i & (-i)
def query(self, r, c):
s = 0
i = r
while i > 0:
j = c
while j > 0:
s += self.tree[i][j]
j -= j & (-j)
i -= i & (-i)
return sDifference Array Template (Range Add, Prefix Reconstruct)
diff = [0] * (n + 1)
# Range add [l, r] += val:
diff[l] += val
diff[r + 1] -= val
# Reconstruct original:
running = 0
for i in range(n):
running += diff[i]
arr[i] += runningDecision Guide
| Scenario | Use |
|---|---|
| Prefix sums only (immutable) | Prefix sum array |
| Point update + prefix sum | BIT |
| Range update + range query | Segment Tree + lazy |
| Min/max range query | Segment Tree |
| 2D range sum | 2D BIT |
| Range add, point query | BIT with difference array |
| Count inversions | BIT + coordinate compression |
Complexity Reference
| Operation | BIT | Segment Tree |
|---|---|---|
| Build | O(n) | O(n) |
| Point update | O(log n) | O(log n) |
| Range query | O(log n) | O(log n) |
| Range update | — | O(log n) with lazy |
| Space | O(n) | O(4n) |
Problem Index
| # | Problem | Key Technique |
|---|---|---|
| 01 | Range Sum Query Mutable | BIT or Seg Tree |
| 02 | Count Smaller Numbers | BIT + coord compress |
| 03 | Reverse Pairs | Merge sort count |
| 05 | Range Module | Disjoint interval set |
| 07 | Count of Range Sum | Merge sort on prefix |
| 08 | Number of LIS | DP with seg tree |
| 09 | Shifting Letters II | Difference array |
| 10 | Range Sum 2D Immutable | 2D prefix sum |
| 11 | My Calendar III | Difference array max |
| 12 | Max Sum Rectangle at most K | BIT + prefix sum |
Key Takeaways
- BIT and Segment Tree both achieve O(log n) for point updates and range queries — the right choice depends on query type.
- BIT uses
i & (-i)to navigate; update walks forward adding this value, query walks backward subtracting it. - Segment Tree works for any associative operation (sum, min, max, GCD) while BIT only supports invertible operations.
- Allocate
4*nnodes for a Segment Tree —2*nonly works for power-of-two sizes. - Lazy propagation defers range updates: mark a node's pending delta, push to children only when accessed.
- 2D BIT extends the lowest-set-bit idea to two nested loops for O(log m * log n) rectangle queries.
- The difference array trick enables O(1) range add and O(n) reconstruction — a lightweight alternative to lazy propagation.
Advertisement
Related reading
Arrays & Strings Complete — 100-Problem Master Cheatsheet6 min readBinary Search Master Recap — All Patterns, Templates & FAANG Cheatsheet8 min readBit Manipulation — Master Recap and Interview Cheatsheet5 min readBFS & DFS Graphs — Master Recap & Cheatsheet4 min readGreedy and Monotonic Stack — Master Recap and Cheatsheet6 min readHashing & Maps Master Recap — All Patterns & Templates5 min read