Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
A Fenwick tree (Binary Indexed Tree) answers prefix-sum queries and point updates in O(log n) each, using a single array and one bit trick. Each index i is responsible for a block of `lowbit(i) = i & -i` elements ending at i. To query prefix(i), jump i down by removing its lowest set bit each step; to update index i, jump up by adding its lowest set bit. It is shorter than a segment tree for the point-update + prefix-sum case. Use 1-based indexing.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you understand the lowbit decomposition — that index i covers the block of size i & -i ending at i — and can explain why update and query walk in opposite directions, all on 1-based indices.
Where candidates slip
Using 0-based indexing so lowbit(0) = 0 spins forever, or moving in the wrong direction (descending on update), or storing absolute values instead of deltas.
How to stand out
Derive the structure from the binary representation: prefix(i) splits into O(log n) blocks, one per set bit of i. Mention that two Fenwick trees give range-update + range-query, and that it is the standard tool for counting inversions.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachExplore the full treeCollapse the tree
How to read it:Index i covers the i & -i elements ending at i. prefix(7) reads bit[7] + bit[6] + bit[4]: blocks of size 1, 2, 4.
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
Baseline — Prefix-Sum Array (Rebuild on Update)
1Hint 1Explore hintHide hint
Precompute prefix[i] = a[1] + … + a[i]. Then prefix(i) is a single array read — O(1).
2Hint 2Explore hintHide hint
But update(i, delta) changes a[i], which shifts prefix[i], prefix[i+1], …, prefix[n] — every later prefix. That is O(n) per update.
3Hint 3Explore hintHide hint
Symmetrically, storing the raw array makes updates O(1) but queries O(n). Neither is good when both operations are frequent.
A · Core idea
The first idea for prefix sums is a precomputed prefix array.
Queries are instant. With prefix[i] stored, prefix(i) is one array read — O(1). For a static array this is unbeatable.
Updates are the problem. Changing a[i] by delta changes every prefix from i onward: prefix[i], prefix[i+1], …, prefix[n] all increase by delta. Repairing them is an O(n) sweep.
Why this fails the problem. The operations are interleaved and there are up to 10⁵ updates. Each O(n) repair gives up to 10¹⁰ total work. The plain (non-prefix) array flips the costs — O(1) update, O(n) query — but is equally bad when queries are frequent.
What the Fenwick tree fixes. We want both sub-linear. The BIT stores partial sums over power-of-two-sized blocks so that an update touches only the O(log n) blocks containing index i, and a query reads only the O(log n) blocks that tile [1, i].
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
a = [_,1,3,5,7] (1-based); prefix array, then update(2,+7)build prefix
prefix = [_,1,4,9,16]
readyquery O(1), update O(n)C · Code with teaching comments
Reference implementation previewclass PrefixArray { private final long[] prefix; private final int n; PrefixArray(int[] a) { // a is 1-based: a[0] unusedExplore the full codeHide the full code
D · Why it works and complexityO(1) query, O(n) update timeO(n) spaceOpen the invariant and proofExplore the proofHide the proof
The prefix-sum array and the raw array are the two extremes: O(1)/O(n) and O(n)/O(1). The Fenwick tree pays a logarithm on both sides by chopping the prefix into blocks whose sizes are powers of two — exactly the set bits of the index.
ComplexityO(1) query, O(n) update time, O(n) space — A prefix-sum array answers queries by one read (O(1)) but any single update invalidates all suffix prefixes, costing O(n) to repair. With q updates that is O(q·n).
E · Edge cases and pitfalls3Thinking O(1) queries make this good enough — updates are O(n) and dominate.Explore edge casesHide edge cases
Pitfalls
- Thinking O(1) queries make this good enough — updates are O(n) and dominate.
- Mixing 0-based and 1-based indexing between the array and prefixes.
- Forgetting that 'set value' differs from 'add delta' when repairing prefixes.
Approach 2 of 2 · Optimal
Fenwick Tree — lowbit Jumps (Optimal)
1Hint 1Explore hintHide hint
Give index i responsibility for the block of elements ending at i with length lowbit(i) = i & -i (the value of its lowest set bit).
2Hint 2Explore hintHide hint
To query prefix(i), repeatedly add bit[i] and strip the lowest set bit: i -= i & -i, until i hits 0. The blocks visited tile [1, i] exactly.
3Hint 3Explore hintHide hint
To update index i, repeatedly add delta to bit[i] and add the lowest set bit: i += i & -i, until i exceeds n. Those are the blocks that contain i.
A · Core idea
A Fenwick tree (Binary Indexed Tree) is an array bit[1..n] where each index secretly owns a block of consecutive elements, and the block boundaries come straight from binary representation.
The lowbit idea. Define lowbit(i) = i & -i. In two's complement, -i flips all bits of i and adds 1, so i & -i isolates the lowest set bit of i. For example lowbit(6) = lowbit(110₂) = 010₂ = 2. We declare that bit[i] stores the sum of the block a[i − lowbit(i) + 1 .. i] — a run of lowbit(i) elements ending at i. So bit[4] (lowbit 4) covers [1,4], bit[6] (lowbit 2) covers [5,6], bit[7] (lowbit 1) covers [7,7].
Why a prefix query is O(log n). To get prefix(i) = a[1..i], start at i and add bit[i] (the block ending at i), then jump to i − lowbit(i) — the index just before that block — and repeat. Each jump removes the lowest set bit, so the number of jumps equals the number of set bits in i, at most log₂ n. The blocks visited are disjoint and tile [1, i] perfectly. For i = 7 = 111₂ we read bit[7] (size 1), bit[6] (size 2), bit[4] (size 4) — three reads for a 7-element prefix.
Why a point update is O(log n). Adding delta to a[i] must update every block that contains i. Those are found by repeatedly adding the lowest set bit: i, i + lowbit(i), …. Each step moves to the next-larger block that swallows the current one, and again there are at most log₂ n such blocks before exceeding n. Update climbs; query descends — opposite directions over the same lowbit step.
Why 1-based indexing is mandatory. lowbit(0) = 0, so an update starting at index 0 would do i += 0 forever. Anchoring the tree at index 1 (and shifting any external 0-based index by +1) avoids the infinite loop and makes the block math clean.
Range sums by subtraction. Because sum is invertible, rangeSum(l, r) = prefix(r) − prefix(l − 1). This is exactly why Fenwick trees are restricted to invertible aggregates: you cannot recover a range minimum by subtracting two prefix minima.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
n = 7, all zero; update(3,+5); then prefix(7)Array state
i=3
bit[3] += 5; next 3 + 1 = 4
prefix(7) = 5C · Code with teaching comments
D · Why it works and complexityO(n) build, O(log n) update, O(log n) query timeO(n) spaceOpen the invariant and proofExplore the proofHide the proof
The whole structure is binary arithmetic in disguise: the set bits of an index tell you both how to split a prefix into blocks (query, descending) and which blocks own an element (update, ascending). That single i & -i operation replaces all the explicit child/parent pointers a segment tree needs — which is why the code is so short and the constant factor so small.
ComplexityO(n) build, O(log n) update, O(log n) query time, O(n) space — Stripping or adding the lowest set bit changes the number of set bits / magnitude so that at most O(log n) iterations occur (one per bit position). Each iteration is O(1). Build can be done in O(n) by propagating each value to its parent, or O(n log n) by n updates.
E · Edge cases and pitfalls7update at index 0 — Bug trap: lowbit(0) = 0 means i never advances → infinite loop. Always keep the tree 1-based.Explore edge casesHide edge cases
Edge cases handled
update at index 0Bug trap: lowbit(0) = 0 means i never advances → infinite loop. Always keep the tree 1-based.prefix(0)Loop condition i > 0 is immediately false → returns 0, the correct empty-prefix sum.rangeSum(1, n)prefix(n) − prefix(0) = whole-array sum; prefix(0) safely returns 0.
Pitfalls
- 0-based indexing — lowbit(0) = 0 causes the update loop to never terminate.
- Swapping the directions — climbing on query or descending on update gives wrong sums.
- Storing absolute values in update instead of deltas — compute delta = new − old to set a value.
- Using int for sums in Java when totals exceed 2³¹ — use long.
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewclass FenwickTree { private final long[] bit; private final int n; FenwickTree(int n) {Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Zero-based indexing infinite loop
lowbit(0) = 0 & -0 = 0, so 'i += i & -i' leaves i at 0 forever. The Fenwick tree must be 1-based; shift any external 0-based index by +1 before calling.
Explore the code comparisonHide the code comparison
Zero-based indexing infinite loop
lowbit(0) = 0 & -0 = 0, so 'i += i & -i' leaves i at 0 forever. The Fenwick tree must be 1-based; shift any external 0-based index by +1 before calling.
Don't write this
def update(self, i, delta): # i passed as 0-based
while i < self.n:
self.bit[i] += delta
i += i & -i # i stays 0 forever when i == 0Write this instead
def update(self, i, delta): # i is 1-based
while i <= self.n:
self.bit[i] += delta
i += i & -i2Wrong jump direction
Update touches the blocks that contain i (climb by adding lowbit); query reads the blocks that tile [1, i] (descend by stripping lowbit). Swapping them reads the wrong cells and returns garbage.
Explore the code comparisonHide the code comparison
Wrong jump direction
Update touches the blocks that contain i (climb by adding lowbit); query reads the blocks that tile [1, i] (descend by stripping lowbit). Swapping them reads the wrong cells and returns garbage.
Don't write this
long prefix(int i) {
long s = 0;
for (; i <= n; i += i & -i) s += bit[i]; // wrong: climbing on a query
return s;
}Write this instead
long prefix(int i) {
long s = 0;
for (; i > 0; i -= i & -i) s += bit[i]; // descend on a query
return s;
}3Absolute value instead of delta
update adds delta into every covering block. Passing the new absolute value double-counts the old contribution. To set a[i] = v, add v − current(i).
Explore the code comparisonHide the code comparison
Absolute value instead of delta
update adds delta into every covering block. Passing the new absolute value double-counts the old contribution. To set a[i] = v, add v − current(i).
Don't write this
def set_value(self, i, v):
self.update(i, v) # adds v on top of the old valueWrite this instead
def set_value(self, i, v):
current = self.range_sum(i, i)
self.update(i, v - current) # add only the differenceRevise
Carry the pattern into the interview
Pattern to remember
Implicit tree over an array via the lowest-set-bit (i & -i) jump
Code pattern to memorize
update(i, delta): # i is 1-based
while i <= n: bit[i] += delta; i += i & -i
prefix(i): # sum of a[1..i]
s = 0
while i > 0: s += bit[i]; i -= i & -i
return sRules to remember
- 1Use 1-based indexing — index 0 has
lowbit(0) = 0, which would loop forever. - 2
lowbit(i) = i & -iisolates the lowest set bit; that value is the size of the block node i covers. - 3update climbs by
i += i & -i(toward n); query descends byi -= i & -i(toward 0). Opposite directions. - 4A range sum
[l, r]isprefix(r) − prefix(l − 1)— two prefix queries. - 5Store deltas in update:
bit[i] += delta, not= val. To set a value, first compute the difference from the current value.
Use this pattern when
- Point updates plus prefix-sum (or range-sum) queries, interleaved
- Counting inversions, or 'how many seen so far are ≤ x' while streaming
- Coordinate-compressed frequency counts with running totals
- When you want segment-tree power for sums but with shorter, cache-friendly code
Choose another pattern when
- Need range min/max, not sum → Fenwick does not support arbitrary range min cleanly; use a segment tree
- Need range updates + range queries → a lazy segment tree is the cleaner fit (or a two-BIT trick)
- Array is static → a prefix-sum array gives O(1) queries with no structure
- Non-invertible aggregate (e.g. max with deletions) → prefix subtraction breaks down
Interview takeaway
The Fenwick tree is the minimalist's segment tree for sums: one array, two five-line loops, and the `i & -i` trick that lets each index own a power-of-two-sized block. It trades the segment tree's generality for half the code and a smaller constant factor.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternFenwick tree / Binary Indexed Tree: an implicit tree over an array where index i owns a block of i & -i elements. Update climbs by adding the lowest set bit, prefix query descends by stripping it — both O(log n). The minimalist tool for point update + prefix/range sum on invertible aggregates (sum, xor).
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Range Sum Query - MutableComing soonThe canonical point-update + range-sum problem — a Fenwick tree is the shortest solution.
- Count of Smaller Numbers After SelfComing soonCoordinate-compress, then a BIT over value-frequencies counts how many smaller values appeared.
- Range Update + Range Query (two BITs)Coming soonMaintain two Fenwick trees to support adding to a range and querying a range sum, both O(log n).
- Segment Tree for Range SumThe more general structure — use it when you need range min/max or lazy range updates.