Overview
A Fenwick tree (binary indexed tree, BIT) supports point updates and prefix sums in O(log n) with a single array of size n + 1 and about ten lines of code. Each index i stores the sum of a block of elements whose length is the lowest set bit of i, computed as i & -i.
To get a prefix sum you jump down by removing the lowest set bit; to update you jump up by adding it. Range sums are prefix(r) - prefix(l - 1). It is the shortest way to solve dynamic prefix-sum and counting problems such as inversions and count of smaller numbers after self.
Instead of storing each day's balance change, some pages summarize 1 day, some 2 days, some 4, some 8. To total the first 13 days you add the 8-day page, a 4-day page, and a 1-day page: three lookups instead of thirteen.
When to use it
- Prefix sums or range sums with point updates.
- Counting inversions or elements smaller / greater than x seen so far.
- Frequency tables with rank queries after coordinate compression.
- You want less code than a segment tree and the operation is invertible (sum, xor).
Problem patterns it solves
Recognize it when: update(i, delta) and sum(0..i) interleaved.
- 307. Range Sum Query - Mutable
- 308. Range Sum Query 2D - Mutable
Recognize it when: for each element, how many earlier (or later) elements are smaller.
- 315. Count of Smaller Numbers After Self
- 1649. Create Sorted Array through Instructions
- 2179. Count Good Triplets in an Array
Recognize it when: count pairs i < j with a[i] > a[j] or a condition on values.
- 493. Reverse Pairs
- 2426. Number of Pairs Satisfying Inequality
Recognize it when: find the kth smallest by binary lifting over the tree.
- 1505. Minimum Possible Integer After at Most K Adjacent Swaps
- Kth smallest in a dynamic multiset
Where it is used in real software
Arithmetic coding compressors keep symbol frequencies in a Fenwick tree to update and query cumulative probabilities quickly.
Computing how many players have a score below x as scores change is a frequency BIT query.
Running totals over buckets (per minute, per price level) that update constantly.
Key terms
- Lowest set bit
- i & -i: the value of the rightmost 1 bit, e.g. 12 & -12 = 4.
- 1-indexed
- Fenwick trees use indexes 1..n; index 0 is unused.
- tree[i]
- Sum of elements (i - lowbit(i), i].
- Coordinate compression
- Map large or negative values to ranks 1..m before using them as indexes.
How it works, step by step
- 1Update(i, delta)
While i <= n: tree[i] += delta; i += i & -i. Moves to every block that contains i.
- 2Prefix(i)
sum = 0; while i > 0: sum += tree[i]; i -= i & -i. Collects disjoint blocks.
- 3Range(l, r)
prefix(r) - prefix(l - 1).
- 4Build
Call update for every element (O(n log n)), or use the O(n) parent-propagation build.
prefix(13) and which blocks it adds
13 in binary is 1101
| i | Binary | i & -i | tree[i] covers | Next i |
|---|---|---|---|---|
| 13 | 1101 | 1 | (12, 13]: element 13 | 13 - 1 = 12 |
| 12 | 1100 | 4 | (8, 12]: elements 9-12 | 12 - 4 = 8 |
| 8 | 1000 | 8 | (0, 8]: elements 1-8 | 8 - 8 = 0, stop |
NOWi: 13 | Binary: 1101 | i & -i: 1 | tree[i] covers: (12, 13]: element 13 | Next i: 13 - 1 = 12
prefix(13) = tree[13] + tree[12] + tree[8]: three lookups, one per set bit of 13. Updates walk the opposite direction: 5 -> 6 -> 8 -> 16 by adding the lowest bit.
Implementation
class FenwickTree { constructor(n) { this.n = n; this.tree = new Array(n + 1).fill(0); // 1-indexed } update(i, delta) { for (; i <= this.n; i += i & -i) this.tree[i] += delta; } prefix(i) { let sum = 0; for (; i > 0; i -= i & -i) sum += this.tree[i]; return sum; } range(l, r) { return this.prefix(r) - this.prefix(l - 1); }} // 307 using 0-indexed API on top of the 1-indexed treeclass NumArray { constructor(nums) { this.nums = nums.slice(); this.bit = new FenwickTree(nums.length); nums.forEach((x, i) => this.bit.update(i + 1, x)); } update(index, val) { this.bit.update(index + 1, val - this.nums[index]); this.nums[index] = val; } sumRange(left, right) { return this.bit.range(left + 1, right + 1); }} // 315. Count of Smaller Numbers After Selffunction countSmaller(nums) { const sorted = [...new Set(nums)].sort((a, b) => a - b); const rank = new Map(sorted.map((v, i) => [v, i + 1])); const bit = new FenwickTree(sorted.length); const result = new Array(nums.length); for (let i = nums.length - 1; i >= 0; i--) { const r = rank.get(nums[i]); result[i] = bit.prefix(r - 1); // how many smaller values are to the right bit.update(r, 1); } return result;}Complexity and performance
One step per bit.
Two prefix queries for a range.
Repeated updates or linear build.
n + 1 numbers.
Trade-offs
Fenwick trees are shorter, faster, and use half the memory, but they naturally support only invertible operations like sum and XOR; min and max with updates are awkward.
The bit tricks are compact but opaque; add a one-line comment on i & -i in production code.
Variants and related techniques
Store differences: update(l, v) and update(r + 1, -v); prefix(i) gives the value at i.
Use two Fenwick trees to handle both linear and constant terms.
Nested loops over both dimensions for mutable 2D range sums in O(log n x log m).
Common mistakes
- Using index 0.
Fix: i & -i is 0 for i = 0, causing an infinite loop. Shift all indexes by 1.
- Updating with the new value instead of the difference.
Fix: Pass delta = newValue - oldValue.
- Large or negative values as indexes.
Fix: Compress coordinates to ranks 1..m first.
Interview questions
What does i & -i compute and why?
It isolates the lowest set bit of i (two's complement makes -i flip all bits above it). That value is the length of the block tree[i] is responsible for.
When would you pick a Fenwick tree over a segment tree?
When you only need point updates with prefix or range sums (or other invertible operations) and want minimal, fast code.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| 307. Range Sum Query - Mutable | Medium | Point update, range sum. |
| 315. Count of Smaller Numbers After Self | Hard | Compression + BIT. |
| 493. Reverse Pairs | Hard | Counting with transformed values. |
| 1649. Create Sorted Array through Instructions | Hard | Smaller and larger counts. |
| 308. Range Sum Query 2D - Mutable | Hard | 2D BIT. |