Fenwick tree

A Fenwick tree or binary indexed tree (BIT) is a data structure that can efficiently update values and calculate prefix sums in an array of values.

Fenwick tree
Binary indexed tree
TypeBionomal tree
Invented1989
Invented byBoris Ryabko
Time complexity in big O notation
Algorithm Average Worst case
Space O(n) O(n)
Search O(logn) O(logn)
Insert O(logn) O(logn)

This structure was proposed by Boris Ryabko in 1989[1] with a further modification published in 1992.[2] It has subsequently become known under the name Fenwick tree after Peter Fenwick, who described this structure in his 1994 article.[3]

When compared with a flat array of values, the Fenwick tree achieves a much better balance between two operations: value update and prefix sum calculation. A flat array of values can either store the values or the prefix sums. In the first case, computing prefix sums requires linear time; in the second case, updating the array values requires linear time (in both cases, the other operation can be performed in constant time). Fenwick trees allow both operations to be performed in time. This is achieved by representing the values as a tree with nodes where the value of each node in the tree is the prefix sum of the array from the index of the parent (inclusive) up to the index of the node (exclusive). The tree itself is implicit and can be stored as an array of values, with the implicit root node omitted from the array. The tree structure allows the operations of value retrieval, value update, prefix sum, and range sum to be performed using only node accesses.

Motivation

Given an array of values, it is sometimes desirable to calculate the running total of values up to each index according to some associative binary operation (addition on integers being by far the most common). Fenwick trees provide a method to query the running total at any index, or prefix sum, in addition to allowing changes to the underlying value array and having all further queries reflect those changes.

Fenwick trees are particularly designed to implement the arithmetic coding algorithm, which maintains counts of each symbol produced and needs to convert those to the cumulative probability of a symbol less than a given symbol. Development of operations it supports were primarily motivated by use in that case.

Description

A Fenwick tree is most easily understood by considering a one-based array with values. The corresponding Fenwick tree has nodes with an implicit node 0 at the root. Each level of the tree contains nodes with indices corresponding to sums of distinct powers of 2 (with representing an empty sum 0). For example, level contains nodes and level contains nodes The parent of a given node can be found by clearing the last set bit (LSB) in its index, corresponding to the smallest power of 2 in its sum. For example, the parent of 6 = 1102 is 4 = 1002.

The below diagram shows the structure of a 16-node Fenwick tree, corresponding to a 15-element array A:

Depiction of a 16-node Fenwick tree containing range sums of a 15-node array A

Let . The value of a node at index corresponds to the range sum of values in , that is, the values of A starting after the parent's index up to the current node's index, inclusive. The values are considered to be the "range of responsibility" for the current node, and consist of (where & denotes bitwise AND) values. Note that the indices in this range do not directly correspond to children of : for example, the range of responsibility for node 2 is but node 1 is not a child of node 2. The root node 0 contains the sum of the empty range with value 0.

The initial process of building the Fenwick tree over an array of values runs in time.

Pseudocode

A simple pseudocode implementation of the two main operations on a Fenwick tree—query and update—is as following:

function query(tree, index) is
    sum := 0
    while index > 0 do
        sum += tree[index]
        index -= lso(index)
    return sum

function update(tree, index, value) is
    while index < size(tree) do
        tree[index] += value
        index += lso(index)

The function computes the least significant 1-bit or last set bit of the given or, equivalently, the largest power of two that is also a divisor of . For example, , as shown in its binary representation: . This function can be simply implemented in code through a bitwise AND operation: lso(n) = n & (-n), assuming a signed integer data type.[3]

Construction

One naive algorithm to construct a Fenwick tree consists of initializing the tree with null values and updating each index individually. This solution works in time, but an construction is possible:[4]

function construct(values) is
    tree := values
    for every index, value in tree do
        parentIndex := index + lso(index)
        if parentIndex < size(tree) then
            tree[parentIndex] += value
    return tree

See also

References

  1. Boris Ryabko (1989). "A fast on-line code" (PDF). Soviet Math. Dokl. 39 (3): 533–537. Archived (PDF) from the original on 2019-07-17. Retrieved 2019-07-17.
  2. Boris Ryabko (1992). "A fast on-line adaptive code" (PDF). IEEE Transactions on Information Theory. 28 (1): 1400–1404. Archived (PDF) from the original on 2019-07-14. Retrieved 2019-07-14.
  3. Peter M. Fenwick (1994). "A new data structure for cumulative frequency tables". Software: Practice and Experience. 24 (3): 327–336. CiteSeerX 10.1.1.14.8917. doi:10.1002/spe.4380240306. S2CID 7519761.
  4. Halim, Steven; Halim, Felix; Effendy, Suhendry. Competitive Programming. Vol. 1 (4th ed.). ISBN 9781716745522.
This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.