3.4.16Trees

Fenwick tree (Binary Indexed Tree) — prefix sums, O(log n) update and query

2,048 words9 min readdifficulty · medium

WHY does this structure exist?


WHAT is stored in each cell? (the lowbit insight)

For an array of values a1,a2,,ana_1, a_2, \dots, a_n (1-indexed), define the Fenwick cell

tree[i]=k=ilowbit(i)+1iak\text{tree}[i] = \sum_{k=i-\text{lowbit}(i)+1}^{i} a_k

where ==lowbit(i)=i&(i)\text{lowbit}(i) = i \,\&\, (-i)== is the value of the lowest set bit of ii.

So cell ii stores the sum of the lowbit(i)\text{lowbit}(i) elements ending at index ii.

ii binary lowbit covers aka_k for kk\in
1 0001 1 [1,1]
2 0010 2 [1,2]
3 0011 1 [3,3]
4 0100 4 [1,4]
5 0101 1 [5,5]
6 0110 2 [5,6]
7 0111 1 [7,7]
8 1000 8 [1,8]
Figure — Fenwick tree (Binary Indexed Tree) — prefix sums, O(log n) update and query

HOW: deriving query from scratch

We want prefix(i)=a1+a2++ai\text{prefix}(i) = a_1 + a_2 + \dots + a_i.

Step 1. Cell ii already gives us the last block: k=ilowbit(i)+1iak\sum_{k=i-\text{lowbit}(i)+1}^{i} a_k. Why? That's literally its definition.

Step 2. What's left is prefix(ilowbit(i))\text{prefix}(i - \text{lowbit}(i)) — the part before that block. Why? Removing the lowbit-sized chunk leaves us needing the prefix up to a smaller index.

Step 3. Recurse: keep adding tree[i]\text{tree}[i] and stripping the lowest bit until i=0i=0. Why this terminates fast? Each step removes one set bit from ii, so it runs at most (number of set bits) log2n\le \log_2 n times.

prefix(i)=tree[i],iilowbit(i) until i=0\boxed{\text{prefix}(i) = \sum \text{tree}[i],\quad i \leftarrow i - \text{lowbit}(i)\ \text{until } i=0}

def query(tree, i):          # prefix sum a[1..i]
    s = 0
    while i > 0:
        s += tree[i]
        i -= i & (-i)        # strip lowest set bit
    return s

HOW: deriving update from scratch

Adding δ\delta to aja_j must fix every cell whose block contains index jj.

Step 1. Cell jj contains aja_j (its block ends at jj). Add δ\delta there. Step 2. The next cell up that contains jj is at index j+lowbit(j)j + \text{lowbit}(j). Why? The next larger block that still includes jj starts at the same left edge but extends further; its index is jj plus its own lowbit, which equals j+lowbit(j)j+\text{lowbit}(j) by the nesting structure of these blocks. Step 3. Repeat while jnj \le n.

update: tree[j]+=δ,jj+lowbit(j) until j>n\boxed{\text{update: } \text{tree}[j] \mathrel{+}= \delta,\quad j \leftarrow j + \text{lowbit}(j)\ \text{until } j>n}

def update(tree, n, j, delta):   # a[j] += delta
    while j <= n:
        tree[j] += delta
        j += j & (-j)            # go to next responsible cell

A range sum [l,r][l, r] is just query(r)query(l1)\text{query}(r) - \text{query}(l-1).


Worked examples


Common mistakes


Complexity

Linear build (the 80/20 trick): initialize tree[i] = a[i], then for each ii from 1 to nn, push your block sum into your parent: if i+lowbit(i) <= n: tree[i+lowbit(i)] += tree[i]. Why? Each cell first becomes complete, then donates its block to the next responsible cell — one pass, O(n)O(n).


What does Fenwick cell tree[i] store?
The sum of a[i-lowbit(i)+1 .. i], a block of length lowbit(i) ending at i.
How do you compute lowbit(i)?
i & (-i), the value of the lowest set bit (uses two's-complement negation).
Query (prefix sum) loop direction?
Add tree[i], then i -= i & (-i), until i == 0 (move DOWN).
Update (point add) loop direction?
Add delta to tree[j], then j += j & (-j), while j <= n (move UP).
Time complexity of update and query?
O(log n) each, since each step changes one bit of the index.
How to get range sum [l, r]?
query(r) - query(l-1).
Why must a Fenwick tree be 1-indexed?
lowbit(0)=0, so loops at index 0 never progress; index 0 has no lowest set bit.
How many cells contain a given element index j?
O(log n) — the chain j, j+lowbit(j), ... up to n.
What is the O(n) build trick?
tree[i]=a[i]; then for each i, if i+lowbit(i)<=n: tree[i+lowbit(i)] += tree[i].
Plain prefix-sum array's weakness that Fenwick fixes?
O(n) per update; Fenwick makes update O(log n) while keeping fast queries.

Recall Feynman: explain to a 12-year-old

Imagine numbered boxes in a row. Instead of asking each box one by one to add a total (slow), some special boxes keep a running subtotal of a chunk of boxes before them. The chunk size is decided by a number trick: look at the rightmost 1 in the box's number written in binary — that tells you how many boxes it summarizes. To get "total up to box 6," you only visit a couple of these subtotal boxes and add them — like grabbing two pre-packed bundles instead of counting candies one by one. To change one candy count, you only fix the few bundles that contain it. Either way: just a handful of steps, never the whole row.

Connections

  • Prefix sum array — the O(1)O(1)-query, O(n)O(n)-update baseline Fenwick improves on.
  • Segment tree — more general (any associative op, range updates), but heavier; Fenwick is the lean special case for sums.
  • Two's complement representation — why i & -i isolates the lowest set bit.
  • Binary representation of integers — set bits = number of query/update steps.
  • Inversion counting / Order statistics — classic Fenwick applications.
  • Range update with difference array — combine with BIT for range-update/point-query.

Concept Map

slow prefix O of n

slow update O of n

motivates

stores in each cell

sets block length

lowest set bit

assembled by

repeatedly

until i=0

affected cells

add delta then

until i greater than n

O of log n

O of log n

Plain array

Need both fast

Prefix-sum array

Fenwick tree

tree i = sum of block ending at i

lowbit i = i AND -i

Strip lowest bit

Prefix query

Point update

Add lowest bit

Both fast

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Fenwick tree ka basic problem ye hai: agar tum ek simple array rakho toh element update karna fast (O(1)) hai par prefix sum nikalna slow (O(n)) hota hai. Aur agar precomputed prefix-sum array rakho toh query fast hai par update slow. Fenwick tree dono ko balance karke O(log n) mein le aata hai — ek hi array se.

Asli trick hai lowbit, yaani i & (-i) — ye number ke binary representation ka sabse last waala set bit deta hai. Har Fenwick cell tree[i] apne se pehle ke lowbit(i) elements ka sum store karta hai, jis ka block i par khatam hota hai. Isliye chhota ya bada block, sab power-of-two size ke hote hain, aur kisi bhi prefix ko sirf thode se blocks se cover kar sakte ho.

Do main operations yaad rakho: query (prefix sum) neeche jaata haii -= i & (-i) karke 0 tak, sum jodte jao. Update upar jaata haij += j & (-j) karke n tak, har responsible cell mein delta add karo. Dono mein sirf jitne set bits utne steps, isliye O(log n). Mnemonic: "Query Quits Down, Update Ups."

Ek important baat: Fenwick tree hamesha 1-indexed rakho, kyunki lowbit(0) = 0 hota hai aur loop kabhi aage nahi badhega. Range sum [l, r] ke liye query(r) - query(l-1) use karo — l-1 isliye kyunki index l ko bhi count karna hai. Inversion counting, competitive programming mein ye structure bahut kaam aata hai, simple aur fast hone ki wajah se.

Go deeper — visual, from zero

Test yourself — Trees

Connections