Skip to main content

Fenwick Tree: Every Rank From One Array

A Fenwick tree, also called a Binary Indexed Tree, is a compact data structure for maintaining an array while answering prefix-sum queries efficiently. A useful way to understand it is through a game leaderboard.

Imagine that leaderboard positions are numbered from 11 through nn. At each position, an array stores a value such as the number of players at that rank, a score contribution, or another quantity that can be added together. Two operations matter:

  1. Change the value at one position.
  2. Ask for the total from the first position through a chosen position.

A plain scan can answer a prefix query, but it may need to inspect many positions. A traditional prefix-sum array makes queries fast, but changing one value can require many stored prefixes to be repaired. A Fenwick tree balances these two requirements. It stores carefully selected partial sums so that both a point update and a prefix query follow short paths through an array.

The central rules are:

  • Every Fenwick node represents a contiguous range.
  • The range length is determined by the node index's lowest set bit, called lowbit.
  • A prefix query repeatedly subtracts lowbit and moves toward the beginning.
  • A point update repeatedly adds lowbit and moves toward larger ranges.

The same quantity controls both directions. A query walks downward by subtracting it, while an update walks upward by adding it.

The leaderboard model

Consider a one-dimensional array of values:

position: 1 2 3 4 5 6 7 8
value: a b c d e f g h

Write the value at position ii as A[i]A[i]. A prefix sum ending at position rr is

P(r)=A[1]+A[2]++A[r]P(r)=A[1]+A[2]+\cdots+A[r]

For example, a query ending at position 66 asks for

P(6)=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]P(6)=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]

A direct scan takes O(r)O(r) time. If many queries arrive, repeatedly scanning the same prefix wastes work. A traditional prefix-sum array stores every P(r)P(r), making a query O(1)O(1). However, if A[3]A[3] changes, every prefix beginning at position 33 or later changes too. Repairing all of those entries can take O(n)O(n) time.

The Fenwick tree stores neither every individual value in every node nor every complete prefix. Instead, it stores a collection of overlapping blocks. A prefix can be assembled from a few blocks that fit together without overlap. The binary representation of an index determines which blocks are used.

The lowbit rule

For a positive index ii, lowbit(i) is the value of the lowest set bit in the binary representation of ii.

Equivalently, using bitwise operations,

lowbit(i)=i&(i)\operatorname{lowbit}(i)=i\mathbin{\&}(-i)

This expression extracts the power of two represented by the rightmost binary 11. Some examples are:

Index iiBinary formlowbit(i)\operatorname{lowbit}(i)
11000111
22001022
33001111
44010044
55010111
66011022
77011111
88100088

The important interpretation is not only the bit value. In a Fenwick tree, the node at index ii stores a range whose length is exactly lowbit(i)\operatorname{lowbit}(i).

Let the Fenwick array be called tree. Its fundamental invariant is

tree[i]=k=ilowbit(i)+1iA[k]\operatorname{tree}[i]=\sum_{k=i-\operatorname{lowbit}(i)+1}^{i}A[k]

In words, the node at ii ends at ii and extends backward by its lowbit length.

For example:

  • tree[1] covers position 11 because its length is 11.
  • tree[2] covers positions 11 through 22 because its length is 22.
  • tree[3] covers position 33 because its length is 11.
  • tree[4] covers positions 11 through 44 because its length is 44.
  • tree[6] covers positions 55 through 66 because its length is 22.
  • tree[8] covers positions 11 through 88 because its length is 88.

For an array of length 88, the coverage is:

NodeLowbitCovered range
1111[1,1][1,1]
2222[1,2][1,2]
3311[3,3][3,3]
4444[1,4][1,4]
5511[5,5][5,5]
6622[5,6][5,6]
7711[7,7][7,7]
8888[1,8][1,8]

The ranges overlap, and that is intentional. A prefix query does not add every overlapping range. It selects a sequence of ranges that fit together exactly.

Why the ranges end at the node index

The right endpoint of tree[i] is always ii. The left endpoint is

L(i)=ilowbit(i)+1L(i)=i-\operatorname{lowbit}(i)+1

The lowbit tells us how large a power-of-two-aligned block ends at ii. For i=6i=6, the binary form is 0110, so the lowbit is 22. The node covers two positions ending at 66:

L(6)=62+1=5L(6)=6-2+1=5

Therefore, tree[6] stores A[5]+A[6]A[5]+A[6].

For i=12i=12, the binary form is 1100, and the lowbit is 44. The node covers four positions:

L(12)=124+1=9L(12)=12-4+1=9

So tree[12] stores the sum over positions 99 through 1212.

This arrangement gives the implicit tree its shape. Odd indices have lowbit 11 and store one-element blocks. Indices divisible by 22 can store larger blocks, and powers of two store especially large prefixes. The structure is represented by an array rather than pointer-linked nodes, but the ranges still form a hierarchy of increasingly large aggregates.

Prefix queries: jumping downward by lowbit

Suppose the request is the sum from position 11 through position rr. Start with index = r and answer = 0.

At every step:

  1. Add tree[index] to the answer.
  2. Replace index with index - lowbit(index).
  3. Stop when the index becomes zero.

The index transition is

indexindexlowbit(index)\operatorname{index}\leftarrow \operatorname{index}-\operatorname{lowbit}(\operatorname{index})

Why does this work? The current node covers the final block of the still-unprocessed prefix. Once that block is added, subtracting its length moves to the position immediately before that block.

Example: prefix through position 77

For r=7r=7, the index sequence is

77lowbit(7)=71=666lowbit(6)=62=444lowbit(4)=44=0\begin{aligned} 7&\rightarrow 7-\operatorname{lowbit}(7)=7-1=6\\ 6&\rightarrow 6-\operatorname{lowbit}(6)=6-2=4\\ 4&\rightarrow 4-\operatorname{lowbit}(4)=4-4=0 \end{aligned}

The visited nodes are 77, 66, and 44. Their ranges are:

  • Node 77 covers [7,7][7,7].
  • Node 66 covers [5,6][5,6].
  • Node 44 covers [1,4][1,4].

Together, they partition the requested prefix:

[1,4]    [5,6]    [7,7]=[1,7][1,4]\;\cup\;[5,6]\;\cup\;[7,7]=[1,7]

Therefore,

P(7)=tree[4]+tree[6]+tree[7]P(7)=\operatorname{tree}[4]+\operatorname{tree}[6]+\operatorname{tree}[7]

No position is counted twice, and no position is omitted.

The pseudocode is:

prefix_sum(index):
answer = 0
while index > 0:
answer += tree[index]
index -= lowbit(index)
return answer

The loop does not move one position at a time. It removes one binary component at each step. That is why the number of iterations is logarithmic in the array length.

Example with numeric values

Suppose the values are:

position: 1 2 3 4 5 6 7
value: 4 2 7 1 3 5 6

The relevant nodes for a prefix through 77 are:

tree[4]=A[1]+A[2]+A[3]+A[4]=4+2+7+1=14tree[6]=A[5]+A[6]=3+5=8tree[7]=A[7]=6\begin{aligned} \operatorname{tree}[4]&=A[1]+A[2]+A[3]+A[4]=4+2+7+1=14\\ \operatorname{tree}[6]&=A[5]+A[6]=3+5=8\\ \operatorname{tree}[7]&=A[7]=6 \end{aligned}

The query returns

P(7)=14+8+6=28P(7)=14+8+6=28

The algorithm reaches the answer by combining stored ranges instead of scanning the seven original values.

Point updates: climbing upward by lowbit

Now consider changing one array position. Suppose the value at position pp changes by an amount called Δ\Delta.

If the old value is replaced by a new value, calculate

Δ=newValueoldValue\Delta=\operatorname{newValue}-\operatorname{oldValue}

Every Fenwick node whose range contains pp must increase by Δ\Delta. Begin at index = p. Add the change to tree[index], then move to the next larger node with

indexindex+lowbit(index)\operatorname{index}\leftarrow \operatorname{index}+\operatorname{lowbit}(\operatorname{index})

Continue while the index is within the Fenwick array.

The pseudocode is:

add(index, delta):
while index <= n:
tree[index] += delta
index += lowbit(index)

This operation receives a delta, not necessarily an entirely new value. If the value at position pp changes from 1010 to 1313, the delta is 33. Adding 1313 to every covering node would be incorrect because those nodes already include the old value.

Example: update position 55

For an array with at least 88 positions, begin at 55.

55+lowbit(5)=5+1=666+lowbit(6)=6+2=888+lowbit(8)=8+8=16\begin{aligned} 5&\rightarrow 5+\operatorname{lowbit}(5)=5+1=6\\ 6&\rightarrow 6+\operatorname{lowbit}(6)=6+2=8\\ 8&\rightarrow 8+\operatorname{lowbit}(8)=8+8=16 \end{aligned}

If n=8n=8, the visited nodes are 55, 66, and 88. Their ranges are:

  • Node 55 covers [5,5][5,5].
  • Node 66 covers [5,6][5,6].
  • Node 88 covers [1,8][1,8].

Each range contains position 55, so each stored sum receives the same delta. The next index, 1616, is outside the array and terminates the update.

Why adding lowbit finds the covering nodes

A Fenwick node is defined by the interval ending at its own index. Starting at position pp, the first node is the smallest stored block ending at pp. Adding the lowbit moves to a larger block that still contains pp.

For position 55, the path is 5685\rightarrow6\rightarrow8. The ranges grow from a single element to two elements and then to the complete eight-element prefix. This is the Fenwick version of climbing from a local contribution to larger aggregate ranges.

The query and update paths are reverse in spirit:

  • A query uses a node, then moves left by subtracting its range length.
  • An update changes a position, then moves right to larger covering nodes by adding their range length.

Neither operation needs explicit pointers. The array index and bit arithmetic determine the path.

A complete small example

Take the values:

position: 1 2 3 4 5 6 7 8
value: 2 1 4 3 6 2 5 1

The Fenwick entries are built from their defined ranges:

tree[1]=A[1]=2tree[2]=A[1]+A[2]=2+1=3tree[3]=A[3]=4tree[4]=A[1]+A[2]+A[3]+A[4]=2+1+4+3=10tree[5]=A[5]=6tree[6]=A[5]+A[6]=6+2=8tree[7]=A[7]=5tree[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]=24\begin{aligned} \operatorname{tree}[1]&=A[1]=2\\ \operatorname{tree}[2]&=A[1]+A[2]=2+1=3\\ \operatorname{tree}[3]&=A[3]=4\\ \operatorname{tree}[4]&=A[1]+A[2]+A[3]+A[4]=2+1+4+3=10\\ \operatorname{tree}[5]&=A[5]=6\\ \operatorname{tree}[6]&=A[5]+A[6]=6+2=8\\ \operatorname{tree}[7]&=A[7]=5\\ \operatorname{tree}[8]&=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]=24 \end{aligned}

Now ask for the prefix through position 66. The query path is 6406\rightarrow4\rightarrow0.

Node 66 contributes positions 55 and 66, while node 44 contributes positions 11 through 44:

P(6)=tree[6]+tree[4]=8+10=18P(6)=\operatorname{tree}[6]+\operatorname{tree}[4]=8+10=18

That matches the original values:

P(6)=2+1+4+3+6+2=18P(6)=2+1+4+3+6+2=18

Next, suppose position 33 increases from 44 to 99. The delta is

Δ=94=5\Delta=9-4=5

The update path is:

33+lowbit(3)=3+1=444+lowbit(4)=4+4=888+lowbit(8)=8+8=16\begin{aligned} 3&\rightarrow 3+\operatorname{lowbit}(3)=3+1=4\\ 4&\rightarrow 4+\operatorname{lowbit}(4)=4+4=8\\ 8&\rightarrow 8+\operatorname{lowbit}(8)=8+8=16 \end{aligned}

Therefore, add 55 to nodes 33, 44, and 88. The affected entries become:

node 3: 4 -> 9
node 4: 10 -> 15
node 8: 24 -> 29

Nodes 33, 44, and 88 all cover position 33. Other nodes do not need modification because their ranges do not contain the changed position.

The prefix through position 66 can now be answered with the same query path:

P(6)=tree[6]+tree[4]=8+15=23P(6)=\operatorname{tree}[6]+\operatorname{tree}[4]=8+15=23

The original prefix was 1818, and increasing one included value by 55 makes the new prefix 2323.

The core invariant

The most important correctness statement is the Fenwick invariant:

tree[i]=k=ilowbit(i)+1iA[k]\operatorname{tree}[i]=\sum_{k=i-\operatorname{lowbit}(i)+1}^{i}A[k]

Every operation should preserve this statement.

The invariant after a point update

Suppose A[p]A[p] increases by Δ\Delta. A node ii needs to change exactly when its interval contains pp:

ilowbit(i)+1pii-\operatorname{lowbit}(i)+1\le p\le i

For every such node, its stored range sum increases by Δ\Delta. The update path visits those relevant nodes in increasing order of their coverage. Thus, after adding Δ\Delta to every visited node, the invariant remains true.

Nodes whose intervals do not contain pp are left untouched, as required.

The invariant during a prefix query

At the beginning of each query iteration, the unprocessed part of the requested prefix ends at the current index. The node at that index covers the final block of that unprocessed part. Adding the node's value accounts for that block. Subtracting its lowbit moves the boundary immediately before the block.

Eventually, the index becomes zero. The visited ranges are disjoint and together cover exactly the original prefix, so their stored sums add to the requested result.

The query is correct because it partitions the prefix; it does not require every node to represent a complete prefix.

Building the Fenwick tree

A Fenwick tree can be initialized by applying add for every array position. If there are nn positions, this repeated-update construction takes O(nlogn)O(n\log n) time.

The direct construction idea is:

for index from 1 through n:
add(index, values[index])

After construction, every node must satisfy the range-sum invariant. Repeated point updates are often the easiest method to understand and trace by hand because they use the same update operation needed later.

The internal Fenwick array is normally one-based. If application data uses zero-based positions, convert an application position to an internal index before performing a query or update. The conversion must be consistent in every operation.

Range sums from two prefixes

The basic Fenwick operation is a prefix sum, but an arbitrary contiguous range can be obtained from two prefixes. For positions ll through rr,

rangeSum(l,r)=P(r)P(l1)\operatorname{rangeSum}(l,r)=P(r)-P(l-1)

For example, the sum over positions 33 through 66 is

A[3]+A[4]+A[5]+A[6]=P(6)P(2)A[3]+A[4]+A[5]+A[6]=P(6)-P(2)

The prefix through 22 removes exactly the values before the requested interval. Since each prefix query takes O(logn)O(\log n) time, the range query also takes O(logn)O(\log n) time.

This formula is especially useful in the leaderboard model. A query can ask for all values through one rank, or it can ask for the total associated with a range of ranks. Both are supported by the same prefix routine.

Why one-based indexing matters

The lowbit operation is normally applied to positive indices. Index zero is special: its lowbit is zero, so a loop that repeatedly adds or subtracts lowbit at index zero would not make progress.

For that reason, a common layout is:

application position: 0 1 2 3
Fenwick index: 1 2 3 4

The exact application-to-internal mapping can differ, but the internal Fenwick indices should normally run from 11 through nn. When translating an application index, apply the same convention to:

  • The original value assignment.
  • The point update.
  • The prefix-query endpoint.
  • The l1l-1 endpoint in a range query.

An off-by-one error can produce plausible results for some positions while corrupting the structure for others. Indexing is therefore part of the data structure's invariant, not merely an implementation detail.

Common mistakes

Treating a node as a complete prefix

tree[i] is generally not the sum from position 11 through position ii. It is the sum over the block ending at ii whose length is lowbit(i).

For example, tree[6] covers positions 55 and 6,notpositions6`, not positions 1throughthrough6.Thecompleteprefixthrough. The complete prefix through 6$ is assembled as tree[6] + tree[4].

Walking one position at a time

A prefix query should not decrement its index by one. The correct transition is

iilowbit(i)i\leftarrow i-\operatorname{lowbit}(i)

Likewise, an update should not increment its index by one. It must use

ii+lowbit(i)i\leftarrow i+\operatorname{lowbit}(i)

These jumps are what make the operations efficient and what match the stored ranges.

Adding a replacement value instead of a delta

If a value changes from xx to yy, the amount to propagate is

Δ=yx\Delta=y-x

Propagating yy would double-count the old contribution already present in the affected nodes.

Forgetting the stopping condition

A query stops when its index reaches zero. An update stops when its index exceeds nn. These conditions are essential for termination.

Mixing zero-based and one-based positions

If the application uses zero-based positions but the Fenwick array uses one-based indices, convert at the boundary. Do not sometimes add one and sometimes omit it. The path generated by lowbit is meaningful only for the internal index used to store the data.

Tracing the structure by hand

When learning or debugging, write down four columns:

Current indexBinary formLowbitNext index
7701111166
6601102244
4401004400

For a query, the next index is current minus lowbit. For an update, it is current plus lowbit. Then list the range represented by each visited node.

For a query through 77:

query index 7: use [7,7]
query index 6: use [5,6]
query index 4: use [1,4]

For an update at 55:

update node 5: change [5,5]
update node 6: change [5,6]
update node 8: change [1,8]

This method makes it easy to check two different properties. First, the ranges used by a query should be disjoint and should cover the requested prefix. Second, every node visited by an update should contain the changed position.

A small array of eight positions is particularly useful because the binary forms remain easy to inspect. For a larger index, the same arithmetic applies; only the number of binary digits increases.

Fenwick tree as binary decomposition

The structure can be viewed as a binary decomposition of a prefix. The binary representation of the endpoint determines which block sizes are needed.

For example, 77 has binary form 0111. Its query path removes the lowest set bit at each step:

76407\rightarrow6\rightarrow4\rightarrow0

The corresponding block lengths are 11, 22, and 44. They add to 77:

1+2+4=71+2+4=7

For endpoint 66, the path is 6406\rightarrow4\rightarrow0. The block lengths are 22 and $4:

2+4=62+4=6

This is why the selected intervals fit perfectly. The query extracts binary-sized suffix blocks from the current prefix, with the lowbit identifying the smallest block removed at each stage.

Updates use the complementary view. Starting at a position, adding lowbit moves into progressively larger ranges that include that position. Binary arithmetic identifies the next aggregate that needs the changed contribution.

This implicit binary structure explains why the data structure is called a tree even though its implementation is an array. The parent-like relationships are encoded by index transitions rather than explicit references.

Leaderboard interpretation

Return to the leaderboard. Suppose each position represents a rank and the array contains a quantity associated with that rank. A prefix query through rank rr asks for the accumulated quantity from the beginning of the leaderboard through rank rr.

If one rank's value changes, the Fenwick tree does not recalculate every prefix from that rank onward. It changes only the stored blocks that cover that rank. Later prefix queries reconstruct the correct total by combining those updated blocks.

For a query endpoint such as rank 77, the tree might combine:

  • The block for rank $7.
  • The block for ranks 55 through 66.
  • The block for ranks 11 through 44.

The original array remains the base sequence, while the Fenwick array provides a structured set of partial sums from which requested prefixes can be assembled. The structure is useful whenever values change at individual positions and the important aggregate is an additive prefix or interval total.

The tree does not require a pointer-linked representation. The array index and its lowbit determine a node's interval, its query predecessor, and the larger nodes affected by an update.

Complexity

For an array of length nn, a prefix query repeatedly removes a lowbit from its index. A point update repeatedly moves to a larger index by adding a lowbit. Both paths have logarithmic length in the standard Fenwick-tree setting.

The usual bounds are:

  • Point update: O(logn)O(\log n).
  • Prefix-sum query: O(logn)O(\log n).
  • Range sum using two prefix sums: O(logn)O(\log n).
  • Storage: O(n)O(n).
  • Repeated-update construction: O(nlogn)O(n\log n).

The important trade-off is clear. The structure uses linear storage and does not make either operation constant time, but it supports both changing one value and asking for an aggregate prefix in logarithmic time.

A compact implementation plan

A reliable implementation can be designed in the following order.

Step 1: Choose the internal indexing convention

Use indices from 11 through nn for the Fenwick array. Decide how application positions map to those indices before writing the query and update routines.

Step 2: Implement lowbit

The standard bitwise form is:

lowbit(index) = index & (-index)

The input should be a positive internal index during normal Fenwick traversal.

Step 3: Implement point addition

Start at the changed position. Add the delta to the current node. Move upward by adding lowbit until the index exceeds nn.

Step 4: Implement prefix sum

Start at the requested endpoint. Add the current node's value. Move downward by subtracting lowbit until the index becomes zero.

Step 5: Implement range sum if needed

Return the prefix through the right endpoint minus the prefix before the left endpoint.

Step 6: Test a tiny array by hand

Use a short array such as eight positions. Write each node's interval, trace a query path, and trace an update path. Small binary examples expose indexing and boundary errors quickly.

Final perspective

The Fenwick tree is compact because it stores the partial ranges needed by its two traversals. The lowbit supplies the shape:

  • At node ii, the stored interval has length lowbit(i)\operatorname{lowbit}(i).
  • A prefix query consumes that interval and moves to ilowbit(i)i-\operatorname{lowbit}(i).
  • A point update propagates the change to larger covering nodes using i+lowbit(i)i+\operatorname{lowbit}(i).

The key invariant is

tree[i]=k=ilowbit(i)+1iA[k]\operatorname{tree}[i]=\sum_{k=i-\operatorname{lowbit}(i)+1}^{i}A[k]

Once that invariant is clear, the algorithms become natural. A query partitions a prefix into disjoint stored blocks. An update changes every stored block that contains the modified position. Both operations follow binary paths of logarithmic length while using only an array.

When working with a leaderboard or any changing sequence of additive values, keep this checklist nearby:

  1. Use positive, consistent internal indices.
  2. Interpret each node as a lowbit-sized range ending at that node.
  3. Query by subtracting lowbit.
  4. Update by adding lowbit.
  5. Propagate a delta for a changed value.
  6. Use two prefixes to obtain an arbitrary range sum.
  7. Verify the invariant on a small example before scaling up.

That combination of an explicit range invariant and implicit binary navigation is what makes the Fenwick tree an efficient tool for dynamic prefix sums.