Fenwick Tree
A Fenwick tree, or binary indexed tree, is an implicit zero-overhead array data structure that uses two's-complement least-significant set bits to compute dynamic prefix sums and point updates in O(log n) time.
Fenwick tree (also known as a Binary Indexed Tree, or BIT) is an implicit, space-optimal array data structure within computation and algorithms that maintains dynamic cumulative frequencies (prefix sums) of an array A[1..n] and executes both prefix queries and point updates in worst-case O(log n) time while using zero auxiliary memory beyond the n-element array itself. Invented in 1989 by Boris Ryabko and independently discovered and popularized in 1994 by Peter M. Fenwick in Software: Practice and Experience to accelerate dynamic symbol probability tables for arithmetic data compression, the Fenwick tree bridges the gap between a raw array (O(1) update, O(n) prefix sum) and a static prefix-sum array (O(n) update, O(1) prefix sum).
By indexing the array from 1 to n and decomposing every integer index i according to the least significant set bit in its binary representation, a Fenwick tree navigates an implicit binomial-like tree hierarchy using a single CPU two's-complement bitwise instruction (i & (โi)) per tree step. Within computational complexity theory and algorithm engineering, a Fenwick tree requires half to one-quarter the memory of a segment tree or balanced AVL tree and achieves high L1/L2 cache locality for dynamic rank queries, inversion counting, arithmetic coding, and Gillespie stochastic chemical simulation.
Binary Index Decomposition and Two's-Complement Bitwise Navigation
Responsibility Length lsb(i) = i & (โi)
Let A[1..n] be a 1-indexed array over an abelian group (G, +, โ, 0). For any positive integer i โ {1, ..., n}, define lsb(i) (lowbit(i)) as the largest power of two 2k dividing iโthe value of the lowest set bit 1 in i. In two's-complement arithmetic, โi = (~i) + 1 inverts all bits above the lowest set bit k while preserving bit k = 1 and trailing zeros, so bitwise AND isolates 2k in one instruction:
In the Fenwick array T[1..n], each slot T[i] stores the sum of a block of length lsb(i) ending at i:
Odd indices (i โก 1 mod 2) have lsb(i) = 1 (T[i] = A[i]), indices i โก 2 (mod 4) store 2-element sums, and powers of two i = 2k store full prefix sums A[1..2k].
Prefix Query (Stripping Lowest Bits) and Point Update (Propagating Carries)
Because i = 2k1 + ... + 2kr (r = popcount(i) โค โlog2(n + 1)โ), the prefix [1, i] partitions into r disjoint blocks. Computing Prefix-Sum(i) = โj=1..i A[j] accumulates T[i] and strips lsb(i) until i = 0:
Conversely, incrementing A[i] by ฮ updates all containing ancestors j โฅ i by repeatedly adding lsb(i) until i > n:
Both operations run in at most โlog2 nโ + 1 steps, and any range sum over [L, R] is Prefix-Sum(R) โ Prefix-Sum(L โ 1).
Linear-Time O(n) Construction, Binary Lifting, and Range-Update Extensions
In-Place O(n) Initialization vs. O(n log n) Insertion
Because each node T[i] has a unique parent p(i) = i + (i & (โi)) in the update tree, copying T[i] โ A[i] and propagating each node's total to its immediate parent in a single forward sweep i = 1..n constructs the Fenwick tree in-place in O(n) time using n โ 1 additions:
Binary Lifting and Dual Fenwick Trees for Range Updates
When all entries of A[1..n] are non-negative (A[i] โฅ 0), finding the smallest index i with Prefix-Sum(i) โฅ W via binary lifting steps k downward from โlog2 nโ to 0 and advances idx โ idx + 2k (subtracting T[idx + 2k] from W) whenever T[idx + 2k] < W, locating the target index in a single O(log n) pass.
To support adding ฮ to a subarray A[L..R] while also querying Prefix-Sum(x), define the difference array D[i] = A[i] โ A[i โ 1]. Expanding the prefix sum algebraically yields:
Maintaining two Fenwick treesโT1 storing D[j] and T2 storing (j โ 1)ยทD[j]โevaluates both range additions and range sums in O(log n) time and 2n space.
| Algorithmic Operation | Raw Array A[1..n] | Prefix-Sum Array P[1..n] | Fenwick Tree T[1..n] | Segment Tree (2nโ4n) |
|---|---|---|---|---|
| Construction Time & Space | O(n) time, n words | O(n) time, n words | O(n) in-place, n words | O(n) time, 2nโ4n words |
| Point Update A[i] += ฮ | O(1) | O(n) | O(log n) (โค log2 n writes) | O(log n) (log2 n writes) |
| Prefix Sum [1..i] / Range Sum [L..R] | O(n) | O(1) | O(log n) (popcount(i) reads) | O(log n) (โค 2 log2 n reads) |
| Order-Statistic Lower Bound (โฅ W) | O(n) | O(log n) binary search | O(log n) via binary lifting | O(log n) tree descent |
| Dynamic Range Minimum Query (RMQ) | O(1) update, O(n) query | Not supported | O(log2 n) (requires 2 BITs) | O(log n) native |
Applications in Data Compression, Genomics, and Stochastic Simulation
Peter Fenwick designed the Binary Indexed Tree for adaptive arithmetic coding and the symbol-frequency stage downstream of the BurrowsโWheeler transform (which he integrated into the bzip2 compression ecosystem alongside Huffman coding): adaptive arithmetic coding updates the cumulative frequency interval [C(s), C(s + 1)) of each symbol s after every byte, which a Fenwick tree executes in O(log |ฮฃ|) time. In non-parametric statistics, sweeping an array A[1..n] while inserting each rank into a Fenwick tree counts array inversions (and computes Kendall's ฯ rank correlation) in O(n log n) time. In chemical kinetics, logarithmic direct Gillespie stochastic simulation algorithms (SSA) store reaction propensities aฮผ in a Fenwick tree and use binary lifting to sample the next firing biochemical reaction in O(log M) time.
Frequently Asked Questions
Why must a Fenwick tree use 1-based indexing instead of 0-based indexing?
In 1-based indexing, every valid index i โ {1, ..., n} has a non-zero least significant set bit lsb(i) = i & (โi) โฅ 1, so both i โ lsb(i) and i + lsb(i) terminate cleanly. If index 0 were passed into the standard bitwise loops, lsb(0) = 0, causing the update loop i โ i + lsb(i) to stall at i = 0.
Why can a single Fenwick tree not answer arbitrary Range Minimum Queries in O(log n) time?
Range sum queries on [L, R] rely on the additive group inverse (โ) so that the prefix [1, L โ 1] can be subtracted from [1, R]. The minimum operator min forms a semigroup without an inverse: knowing min(A[1..R]) and min(A[1..L โ 1]) does not determine min(A[L..R]) whenever the global minimum lies in [1, L โ 1], requiring a segment tree (O(log n)) instead.
References
- Ryabko, B. Y. (1989). "A fast on-line code." Soviet Mathematics Doklady, 39(3), 533โ537. https://mathscinet.ams.org/mathscinet-getitem?mr=1001696
- Fenwick, P. M. (1994). "A new data structure for cumulative frequency tables." Software: Practice and Experience, 24(3), 327โ336. https://doi.org/10.1002/spe.4380240306
- Fenwick, P. M. (1996). "Block sorting text compressionโfinal report." The Computer Journal, 39(9), 731โ740. https://doi.org/10.1093/comjnl/39.9.731
- Mishra, P. (2013). "A new algorithm for updating and querying sub-arrays of multidimensional arrays." Journal of Cloud Computing, 2013, 1โ8. https://doi.org/10.48550/arXiv.1311.6093
- Pibiri, G. E., & Venturini, R. (2020). "Practical trade-offs for the prefix-sum problem." Software: Practice and Experience, 51(5), 921โ949. https://doi.org/10.1002/spe.2918