TREES, TRIES & HEAPS / ALGORITHM BRIEF

Fenwick tree

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.

AdvancedPhase 04 / Topic 8 of 8Mental modelComplexityEdge cases
01

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.

Bank statements by powers of two

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.

02

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).
03

Problem patterns it solves

Dynamic prefix sums

Recognize it when: update(i, delta) and sum(0..i) interleaved.

  • 307. Range Sum Query - Mutable
  • 308. Range Sum Query 2D - Mutable
Counting smaller / larger so far

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
Inversions and pairs

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
Order statistics on a frequency BIT

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
04

Where it is used in real software

Cumulative frequency tables

Arithmetic coding compressors keep symbol frequencies in a Fenwick tree to update and query cumulative probabilities quickly.

Leaderboards and ranks

Computing how many players have a score below x as scores change is a frequency BIT query.

Analytics counters

Running totals over buckets (per minute, per price level) that update constantly.

05

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.
06

How it works, step by step

  1. 1
    Update(i, delta)

    While i <= n: tree[i] += delta; i += i & -i. Moves to every block that contains i.

  2. 2
    Prefix(i)

    sum = 0; while i > 0: sum += tree[i]; i -= i & -i. Collects disjoint blocks.

  3. 3
    Range(l, r)

    prefix(r) - prefix(l - 1).

  4. 4
    Build

    Call update for every element (O(n log n)), or use the O(n) parent-propagation build.

07

prefix(13) and which blocks it adds

13 in binary is 1101

Step 1 / 3
iBinaryi & -itree[i] coversNext i
1311011(12, 13]: element 1313 - 1 = 12
1211004(8, 12]: elements 9-1212 - 4 = 8
810008(0, 8]: elements 1-88 - 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.

08

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;}
09

Complexity and performance

UpdateO(log n)

One step per bit.

Prefix / range sumO(log n)

Two prefix queries for a range.

BuildO(n log n) or O(n)

Repeated updates or linear build.

SpaceO(n)

n + 1 numbers.

10

Trade-offs

Fenwick vs segment tree

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.

Readability

The bit tricks are compact but opaque; add a one-line comment on i & -i in production code.

11

Variants and related techniques

Range update, point query

Store differences: update(l, v) and update(r + 1, -v); prefix(i) gives the value at i.

Range update, range query

Use two Fenwick trees to handle both linear and constant terms.

2D Fenwick tree

Nested loops over both dimensions for mutable 2D range sums in O(log n x log m).

12

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.

13

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.

14

Practice problems

ProblemDifficultyWhat it trains
307. Range Sum Query - MutableMediumPoint update, range sum.
315. Count of Smaller Numbers After SelfHardCompression + BIT.
493. Reverse PairsHardCounting with transformed values.
1649. Create Sorted Array through InstructionsHardSmaller and larger counts.
308. Range Sum Query 2D - MutableHard2D BIT.