Skip to content

Fenwick Tree (Binary Indexed Tree)

A Fenwick tree answers “prefix sum up to index i” and “add delta at index i” in O(logn)O(\log n) each, using one flat array and a single bitwise trick — no recursion, no explicit tree nodes, and about fifteen lines of code.

  • The lowbit trick, i & (-i), and why it isolates the lowest set bit of i.
  • How that one trick gives O(logn)O(\log n) point update and O(logn)O(\log n) prefix-sum query.
  • Getting an arbitrary range sum from two prefix queries.
  • Why a Fenwick tree is simpler but less general than a segment tree, and when to reach for which.
  • A brief look at extending it to 2D.

Every positive integer, in two’s-complement binary, has the property that i & (-i) isolates its lowest set bit — the smallest power of two that divides it:

lowbit.py
def lowbit(i):
    return i & (-i)
 
 
for i in [1, 2, 3, 4, 6, 12, 24]:
    print(f"i={i:>2} ({i:>05b})  lowbit={lowbit(i)}")
text
>>> bin(12)
'0b1100'
>>> lowbit = 12 & (-12)
>>> lowbit
4
>>> bin(lowbit)
'0b100'

Every Fenwick tree index i (the tree is conventionally 1-indexed) is made “responsible” for summing exactly lowbit(i) elements, ending at i itself:

Index ilowbit(i)Range it’s responsible for
11[1, 1]
22[1, 2]
31[3, 3]
44[1, 4]
51[5, 5]
62[5, 6]
71[7, 7]
88[1, 8]

Connecting each index i to i + lowbit(i) (the index that will next need to know about a change at i) turns this table into an actual tree:

diagram Fenwick tree structure for n=8 -- edges follow i += lowbit(i), the update-chain jump mermaid

Update walks up this tree (i += lowbit(i), toward the root) to touch every ancestor whose range includes i. Query walks down — really, it walks toward 0 (i -= lowbit(i)), accumulating sums from disjoint ranges that together cover [1, i].

sketch Update chain (amber, walks up) vs. prefix-sum chain (blue, walks toward 0) p5.js
update(3) touches 3 -> 4 -> 8. prefix_sum(6) touches 6 -> 4, then stops at 0.
fenwick_tree.py
class FenwickTree:
    """1-indexed Binary Indexed Tree: O(log n) point update, O(log n) prefix-sum query."""
 
    def __init__(self, n):
        self.n = n
        self.tree = [0] * (n + 1)   # tree[0] is unused -- indices are 1..n
 
    def update(self, i, delta):
        """Add `delta` to the value at position i (1-indexed)."""
        while i <= self.n:
            self.tree[i] += delta
            i += i & (-i)          # walk UP to the next index this one feeds into
 
    def prefix_sum(self, i):
        """Sum of elements 1..i (1-indexed, inclusive)."""
        total = 0
        while i > 0:
            total += self.tree[i]
            i -= i & (-i)          # walk toward 0, one disjoint range at a time
        return total
 
    def range_sum(self, left, right):
        """Sum over the inclusive 1-indexed range [left, right]."""
        return self.prefix_sum(right) - self.prefix_sum(left - 1)
 
    @classmethod
    def from_list(cls, values):
        """Build in O(n log n) via repeated point updates (simple; an O(n) build exists too)."""
        bit = cls(len(values))
        for i, v in enumerate(values, start=1):
            bit.update(i, v)
        return bit
 
 
values = [3, 2, -1, 6, 5, 4, -3, 3]     # conceptually 1-indexed: position 1..8
bit = FenwickTree.from_list(values)
 
print("sum of first 5 elements:", bit.prefix_sum(5))         # 3+2-1+6+5 = 15
print("sum of elements 3..6:", bit.range_sum(3, 6))          # -1+6+5+4 = 14
 
bit.update(3, 10)                                             # position 3: -1 -> 9
print("sum of elements 3..6 after point update:", bit.range_sum(3, 6))   # 9+6+5+4 = 24

range_sum is where the “invertible” requirement shows up: sum(right) - sum(left - 1) only works because subtraction can undo addition. There’s no equivalent trick for min or max — you can’t “subtract” a minimum back out of a combined range — which is exactly why a Fenwick tree can’t replace a segment tree for those operations.

values = [3, 2, -1, 6, 5, 4, -3, 3], 1-indexed. After building, tree holds:

ibinarylowbit(i)covers positionsvalue
1000111..13
2001021..25
3001113..3−1
4010041..410
5010115..55
6011025..69
7011117..7−3
8100081..819

The single rule to memorise: tree[i] covers the lowbit(i) positions ending at i — that is, the range (i − lowbit(i), i]. Everything else follows from it. Note that odd indices cover exactly one element, and index 8 covers the whole array, so the structure is neither “one node per element” nor a balanced tree — it is a set of ranges chosen so the bit arithmetic works out.

Query — prefix_sum(7). Walk down, subtracting lowbit each time:

ireads tree[i]coversrunning totalnext i = i − lowbit(i)
7−37..7−37 − 1 = 6
695..666 − 2 = 4
4101..4164 − 4 = 0 → stop

Answer 16, and check the decomposition: 1..4 + 5..6 + 7..7 = 1..7, exactly — three disjoint ranges, no overlap and no gap. That is the invariant, and it is why the query is O(logn)O(\log n): each step clears one set bit of i, and 7 = 0111 has three.

Update — update(3, +4). Walk up, adding lowbit each time:

iadds 4 to tree[i]because tree[i] covers position 3next i = i + lowbit(i)
3−1 → 33..3 ✓3 + 1 = 4
410 → 141..4 ✓4 + 4 = 8
819 → 231..8 ✓8 + 8 = 16 > n → stop

Three nodes touched — exactly the ones whose range contains position 3. prefix_sum(7) now returns 20, up by 4 as it must be.

  • Update and query walk in opposite directions, and that is the whole design. i + lowbit(i) enumerates the ranges containing i; i − lowbit(i) enumerates the disjoint ranges tiling the prefix. One trick, used two ways.
  • Neither walk touches more than log2n\log_2 n nodes, because each step either clears a set bit (query) or carries one (update).
  • tree[5] was never touched by update(3, …) — position 3 is not in 5..5. Updating everything to the right would be O(n)O(n); the point of the structure is knowing precisely which O(logn)O(\log n) nodes care.
  • 1-indexing is mandatory. lowbit(0) = 0, so an update at index 0 would loop forever (0 + 0 = 0) and a query at 0 would terminate immediately with 0. That is why tree[0] is unused and every public API here is 1-indexed — the single most common Fenwick bug is passing a 0-indexed position straight through.
AspectFenwick tree (BIT)Segment tree
Code size~15 lines, no recursion~40-60 lines
Supported operationsInvertible ops only (sum, xor)Any associative op (sum, min, max, gcd, custom)
Range update + range queryAwkward (needs a second BIT)Native, via lazy propagation
Memoryn + 1 ints~2n (iterative) to ~4n (recursive)
Conceptual overheadOne bitwise trickTree recursion + merge/identity abstraction

When the operation is a plain sum (or xor) and only one of “range update” / “range query” is ever a range (the other being a point), the Fenwick tree wins on simplicity every time.

The same lowbit trick nests cleanly into two dimensions: update and query both become a double loop, one lowbit walk per axis.

fenwick_tree_2d.py
class FenwickTree2D:
    """2D BIT: point update + prefix-rectangle-sum query, O(log rows * log cols) each."""
 
    def __init__(self, rows, cols):
        self.rows = rows
        self.cols = cols
        self.tree = [[0] * (cols + 1) for _ in range(rows + 1)]
 
    def update(self, r, c, delta):
        i = r
        while i <= self.rows:
            j = c
            while j <= self.cols:
                self.tree[i][j] += delta
                j += j & (-j)
            i += i & (-i)
 
    def prefix_sum(self, r, c):
        """Sum of the rectangle from (1, 1) to (r, c), inclusive, 1-indexed."""
        total = 0
        i = r
        while i > 0:
            j = c
            while j > 0:
                total += self.tree[i][j]
                j -= j & (-j)
            i -= i & (-i)
        return total
 
 
grid = FenwickTree2D(4, 4)
grid.update(2, 3, 5)   # point (2,3) += 5
grid.update(4, 4, 7)   # point (4,4) += 7
 
print("prefix sum up to (3, 3):", grid.prefix_sum(3, 3))   # only (2,3) is inside -> 5
print("prefix sum up to (4, 4):", grid.prefix_sum(4, 4))   # both points inside -> 12

The same “subtract off what shouldn’t be counted” idea from 1D range sums extends to rectangles too (inclusion-exclusion over four prefix queries), which is exactly the shape of Range Sum Query 2D - Mutable.

OperationTime
Build (via n point updates)O(nlogn)O(n \log n)
Point updateO(logn)O(\log n)
Prefix-sum queryO(logn)O(\log n)
Range-sum query (two prefix queries)O(logn)O(\log n)
2D point update / prefix queryO(log(rows)log(cols))O(\log(\text{rows}) \cdot \log(\text{cols}))
Problem / needWhat changesCanonical problem
Prefix / range sum with point updatesthe base templateLC 307 Range Sum Query — Mutable
Count smaller elements to the rightindex the tree by value, not position; sweep the array right-to-left and query the prefix of the value rangeLC 315
Count inversions / reverse pairssame value-indexed sweep, counting how many already-seen values exceed the current oneLC 493
Values are huge or negativecoordinate-compress first: sort the distinct values, map each to its rank, index the tree by rankLC 315, LC 493
Range update, point querystore a difference array in the Fenwick tree: add(l, +v), add(r+1, -v), and a point query becomes a prefix sum
Range update, range querytwo Fenwick trees (one for the linear term, one for the constant) — the standard “BIT with range updates” construction
2D prefix sums with updatesa Fenwick tree of Fenwick trees; both walks nest, giving O(logmlogn)O(\log m \log n)LC 308 (Premium)
XOR instead of sumreplace += with ^=; XOR is its own inverse, so prefix(r) ^ prefix(l-1) works
k-th smallest / order statisticdescend the tree bit by bit from the highest power of two, giving O(logn)O(\log n) instead of O(log2n)O(\log^2 n) binary search over prefix sums
Min / max over a rangenot a Fenwick tree — min is not invertible, so no prefix subtraction exists. Segment tree, or a sparse table if the array is static
They askWhat they’re checkingThe answer
“What does tree[i] actually hold?”Whether you understand the structure or memorised the loopsThe sum of the lowbit(i) elements ending at i, i.e. the range (i − lowbit(i), i]. tree[8] covers the whole 8-element array; tree[7] covers only position 7
“Why does update walk up and query walk down?”The core insighti + lowbit(i) enumerates the ranges containing i — those are the nodes an update must fix. i − lowbit(i) enumerates the disjoint ranges that tile the prefix — those are the nodes a query must read. One trick, two directions
“Why is it O(logn)O(\log n)?”PrecisionEach step of the query clears one set bit of i; each step of the update carries one. Neither can happen more than log2n\log_2 n times
“Fenwick or segment tree?”JudgementFenwick if the operation is invertible (sum, XOR, count) and updates are point updates — it is ~10 lines, uses one array of size n+1, and has excellent constants. Segment tree the moment you need min/max/gcd, range assignment, or lazy propagation
“Why 1-indexed?”The trapBecause lowbit(0) = 0: an update at 0 loops forever and a query at 0 returns immediately. tree[0] is deliberately unused
“Now count smaller elements to the right (LC 315)”Whether you can invert the mental modelIndex the tree by value instead of position. Sweep right-to-left; at each element, prefix_sum(value − 1) is how many smaller values have already been seen, then insert the current value. Coordinate-compress first if values are large
“Build in O(n)O(n) rather than O(nlogn)O(n \log n)DepthCopy the values into tree[1..n], then for each i in increasing order push tree[i] into its parent i + lowbit(i) if that is in range. One pass, no logs
“Can it do range minimum?”Knowing the boundaryNot usefully. Range sum works because prefix(r) − prefix(l−1) inverts the combination, and min has no inverse. A Fenwick tree can do prefix-min with only-decreasing updates, which is a real but narrow special case

The first is the Fenwick tree with nothing added. The other two are the trick that makes it a competitive-programming staple: index by rank, store counts, and a prefix sum becomes “how many values below this one have I seen”.

LC 307 — Range Sum Query - Mutable · Medium

Section titled “LC 307 — Range Sum Query - Mutable · Medium”

Problem. Implement NumArray(nums) with update(index, val) setting an element and sumRange(left, right) returning the inclusive sum.

Constraints. 1 <= len(nums) <= 3 * 10**4, -100 <= nums[i], val <= 100, up to 3 * 10**4 calls.

Examples. NumArray([1,3,5]): sumRange(0,2) gives 9, then update(1,2), then sumRange(0,2) gives 8

Editorial · approach, complexity, follow-ups

The problem exists to make you choose a structure. A plain array gives O(1)O(1) update and O(n)O(n) query; a prefix-sum array gives O(1)O(1) query and O(n)O(n) update. With both operations called 3×1043 \times 10^4 times, either is 10910^9 operations. A Fenwick tree makes both O(logn)O(\log n).

Time O(nlogn)O(n \log n) to build, O(logn)O(\log n) per operation. Space O(n)O(n).

  • update sets, it does not add. Storing the current values and applying val - self.nums[index] is the whole adaptation. Adding val directly is the most common wrong answer and it corrupts everything after the first update.
  • One-indexing is not decoration. i & -i is 0 when i is 0, so index 0 would loop forever. Converting with i += 1 on the way in is why the public interface can stay zero-indexed.
  • i & -i is the lowest set bit, so it is the size of the range that node covers. += walks to the next node that includes i; -= walks to the predecessor block. Being able to say what a node covers is what separates understanding from copying.
  • _prefix(left), not _prefix(left + 1), for an inclusive range. Off by one here is silent — it just gives wrong sums.
  • Building by n updates is O(nlogn)O(n \log n). The O(n)O(n) build exists: fill the tree with the raw values, then for each i push tree[i] into tree[i + (i & -i)]. Worth mentioning.

Follow-ups you should expect: “Range update, point query?” — store deltas instead of values: add at left, subtract at right + 1, and a point query is a prefix sum. “Range update, range query?” — two Fenwick trees, or a segment tree with lazy propagation. “Range minimum instead of sum?” — a Fenwick tree cannot do it cleanly, because min has no inverse and prefix subtraction is meaningless; use a segment tree. That contrast is the most-asked question about Fenwick trees. “2D?” — nested trees, O(log2n)O(\log^2 n) per operation.

LC 315 — Count of Smaller Numbers After Self · Hard

Section titled “LC 315 — Count of Smaller Numbers After Self · Hard”

Problem. Return an array counts where counts[i] is the number of elements to the right of nums[i] that are smaller than it.

Constraints. 1 <= len(nums) <= 10**5, -10**4 <= nums[i] <= 10**4.

Examples. [5,2,6,1] gives [2,1,1,0] · [-1] gives [0] · [-1,-1] gives [0,0]

Editorial · approach, complexity, follow-ups

The reframing is the lesson: a Fenwick tree does not have to store the array. Here the index is a value’s rank and the cell is a count, so prefix(r - 1) answers “how many of the values I have inserted so far are smaller than this one”. Sweeping right to left means “inserted so far” is exactly “to the right of me”.

Time O(nlogn)O(n \log n). Space O(n)O(n).

  • Coordinate compression is mandatory. Values are negative and the range is 20,001 wide — workable here by shifting, but the habit generalises to values up to 10910^9 where an array indexed by value is impossible.
  • prefix(r - 1), not prefix(r). Strictly smaller excludes equal values, which is why [-1,-1] must give [0,0] and not [1,0].
  • Right to left. Sweeping the other way answers a different question (smaller numbers before self), and you would have to reverse and re-derive.
  • Query before insert. Inserting first would count the element against itself.
  • A single element gives [0] — nothing is to its right.

Three other solutions worth naming, because interviewers often want an alternative: merge sort counting inversions during the merge, a balanced BST / order statistic tree, and in Python a SortedList with bisect_left, which is O(nn)O(n \sqrt{n}) but three lines long and usually fast enough. Say the Fenwick version is what you would write and the SortedList is what you would reach for under time pressure.

Follow-ups you should expect: “Count greater to the right?” — query the suffix instead, or negate the values. “Count smaller to the left?” — sweep the other way. “Count of Range Sum (LC 327)?” — the same technique over prefix sums instead of raw values. “Reverse Pairs (LC 493)?” — also this, with a doubled comparison. “Streaming input?” — Fenwick handles it directly, since inserts are incremental.

LC 1649 — Create Sorted Array through Instructions · Hard

Section titled “LC 1649 — Create Sorted Array through Instructions · Hard”

Problem. Insert the elements of instructions one at a time into a new container, keeping it sorted. The cost of each insertion is the minimum of the number of elements already present that are strictly less than it and the number strictly greater. Return the total cost modulo 10**9 + 7.

Constraints. 1 <= len(instructions) <= 10**5, 1 <= instructions[i] <= 10**5.

Examples. [1,5,6,2] gives 1 · [1,2,3,6,5,4] gives 3 · [1,3,3,3,2,4,2,1,2] gives 4

Editorial · approach, complexity, follow-ups

The same count-by-rank Fenwick tree as the previous problem, sweeping left to right because “already inserted” now means “earlier in the instruction list”.

The arithmetic that carries the whole problem: after k insertions, prefix(v) counts everything <= v, so

greater=kprefix(v),smaller=prefix(v1)\text{greater} = k - \text{prefix}(v), \qquad \text{smaller} = \text{prefix}(v-1)

and the duplicates — the values exactly equal to v — are excluded from both, as the definition of cost requires.

Time O(nlogM)O(n \log M) where MM is the maximum value. Space O(M)O(M).

  • Duplicates are the whole difficulty. [1,3,3,3,2,4,2,1,2] gives 4, and it is the only test here that distinguishes a correct duplicate rule from k - prefix(v - 1), which would count equal elements as greater.
  • No coordinate compression needed, since values are 1..10**5. Compressing anyway is harmless and would be required if the bound were 10910^9.
  • k is the count already inserted, which enumerate gives for free — at iteration k exactly k items are in the container.
  • Modulus at the end, not inside. min(smaller, larger) must be compared on true values; reducing mid-loop cannot change these magnitudes in Python, but taking the modulus before a comparison is a real bug pattern in languages that overflow, and worth flagging.
  • A single instruction costs 0 — nothing is present to compare against.

Follow-ups you should expect: “Values up to 10910^9?” — compress first. “Cost = number of inversions created instead?” — that is LC 315’s count, without the min. “Report the running cost after each insertion?” — accumulate and emit; no change to the structure. “Use a SortedList?” — bisect_left and bisect_right give both counts in O(logn)O(\log n) with O(n)O(n) insertion, which passes here and is far shorter. “Why is min there at all?” — it models inserting from whichever end is closer, which is what makes the problem interesting rather than just an inversion count.

Generated from the problem database, so each entry carries its sheet membership and reported companies. Tick them off as you go — progress is saved in this browser, and the Export button writes it to a file you can keep.

4 problems
0 easy2 medium2 hard

Work down the ladder. Tick each problem off as you go — progress is saved in this browser, and the Export button in the filter bar writes it to a file you can keep.

  • 307Range Sum Query - MutablemediumThe same problem as the segment tree lesson, solvable here in far fewer lines
  • 673Number of Longest Increasing SubsequencemediumA Fenwick-tree variant (indexed by value, storing "best length" / "count of ways" pairs) that answers each element's transition in $O(\log n)$ instead of the classic $O(n^2)$ DP
  • 315Count of Smaller Numbers After SelfhardA Fenwick tree over coordinate-compressed values, counting smaller elements seen so far while scanning right to left
  • 1649Create Sorted Array through InstructionshardInsert one element at a time, using a Fenwick tree over compressed values to count "how many smaller" and "how many larger" already-inserted elements in $O(\log n)$
pch.quizTag Fenwick tree — self-check
  1. What does `tree[i]` store?

    pch.quizShowAnswer

    B — The sum of the lowbit(i) elements ending at i — the range (i − lowbit(i), i] — so tree[8] covers all of 1..8 while tree[7] covers only position 7 — It is neither one node per element nor a balanced tree. The ranges are chosen precisely so that any prefix is a union of O(log n) of them and any position lies in O(log n) of them.

  2. Why does update walk `i += i & -i` while query walks `i -= i & -i`?

    pch.quizShowAnswer

    B — Because i + lowbit(i) enumerates the ranges CONTAINING i — the nodes an update must fix — while i − lowbit(i) enumerates the disjoint ranges TILING the prefix, which are the nodes a query must read — One bit trick used in two directions. prefix_sum(7) reads tree[7] + tree[6] + tree[4] = ranges 7..7, 5..6, 1..4 — disjoint, no gaps.

  3. Why must a Fenwick tree be 1-indexed?

    pch.quizShowAnswer

    B — Because lowbit(0) = 0: an update at index 0 would loop forever (0 + 0 = 0) and a query at 0 would return immediately — Passing a 0-indexed position straight through is the single most common Fenwick bug. tree[0] is deliberately unused.

  4. Can a Fenwick tree answer range MINIMUM queries?

    pch.quizShowAnswer

    B — Not usefully — range sum works because prefix(r) − prefix(l−1) inverts the combination, and min has no inverse. Use a segment tree, or a sparse table for a static array — Invertibility is the precondition. (Prefix-min with only-decreasing updates is a real but narrow special case, worth mentioning as a caveat rather than a solution.)

  5. LC 315 asks how many elements to the right of each position are smaller. How is that a Fenwick problem?

    pch.quizShowAnswer

    B — Index the tree by VALUE rather than position: sweep right-to-left, and prefix_sum(value − 1) counts the smaller values already seen; coordinate-compress first if values are large or negative — Inverting the axis — value-indexed instead of position-indexed — is what turns the whole inversion-counting family (LC 315, LC 493) into this structure.

  6. The array is static and you need many range sums. Fenwick tree?

    pch.quizShowAnswer

    B — No — a plain prefix-sum array answers each query in O(1) after an O(n) build. A Fenwick tree only earns its log factor when the array is mutated between queries — Reaching for the fancier structure when the simple one strictly dominates is a real interview tell. The Fenwick tree buys you updates, and nothing else.

  • Cue — prefix or range sums on an array that changes between queries; or inversion-counting (“how many smaller to the right”).
  • tree[i] = sum of the lowbit(i) elements ending at i, i.e. the range (i − lowbit(i), i]. That one sentence generates the whole structure.
  • lowbit(i) = i & -i — isolates the lowest set bit.
  • Updatewhile i <= n: tree[i] += delta; i += i & -i (walk up, the ranges containing i).
  • Querywhile i > 0: total += tree[i]; i -= i & -i (walk down, the disjoint ranges tiling the prefix).
  • Range sumprefix(r) - prefix(l-1). Requires an invertible operation: sum, XOR, count.
  • 1-indexed, alwayslowbit(0) = 0 loops forever.
  • CostO(logn)O(\log n) update and query, O(n)O(n) space, O(nlogn)O(n \log n) naive build or O(n)O(n) with the push-to-parent build.
  • Inversion problems — index by value, sweep the array, coordinate-compress first.
  • Not for min/max/gcd — no inverse, so use a segment tree or, if static, a sparse table.
  • i & (-i) isolates the lowest set bit of i — the size of the range index i is responsible for.
  • Update walks up (i += lowbit(i)); prefix query walks toward zero (i -= lowbit(i)); both are O(logn)O(\log n).
  • Range sum is just two prefix queries subtracted, which only works because addition is invertible — min/max can’t do this, which is the Fenwick tree’s one real limitation next to a segment tree.
  • A 2D Fenwick tree nests the same trick along both axes for rectangle sums, at O(log(rows)log(cols))O(\log(\text{rows}) \cdot \log(\text{cols})) per operation.

Between this lesson and the previous one, the segment tree and the Fenwick tree cover almost every “range query + update” problem competitive programming throws at you — reach for the Fenwick tree first when the operation is a plain sum, and the segment tree when it isn’t (or when range updates and range queries both need to be ranges).

Next: Sparse Tables and Range Minimum Query — when the array is static (no updates at all), an even faster O(1)O(1)-per-query structure becomes possible.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading