0
0
Data Structures Theoryknowledge~6 mins

Fenwick trees (Binary Indexed Trees) in Data Structures Theory - Full Explanation

Choose your learning style9 modes available
Introduction
Imagine you have a long list of numbers and you want to quickly find the sum of any part of the list or update a number without adding everything again. Doing this by adding each time can be slow. Fenwick trees solve this problem by organizing data to make these operations fast and efficient.
Explanation
Structure of Fenwick Tree
A Fenwick tree is a special array that stores partial sums of the original list. Each position in this array covers a range of elements from the original list, and these ranges overlap in a way that allows quick calculation of sums. The tree uses binary representation of indices to decide which elements to include in each partial sum.
Fenwick trees store overlapping partial sums to enable fast sum queries.
Prefix Sum Query
To find the sum of elements from the start up to a certain position, the Fenwick tree uses a process that jumps through the tree by removing the last set bit of the index repeatedly. This lets it add only a few stored sums instead of all elements, making the query very fast.
Prefix sums are computed by combining a few stored partial sums using binary index jumps.
Updating Values
When a value in the original list changes, the Fenwick tree updates its stored sums by moving forward through the tree. It adds the change to all relevant positions that cover the updated element, again using binary operations to find these positions quickly.
Updates propagate through the tree by adding changes to all relevant partial sums efficiently.
Efficiency and Use Cases
Fenwick trees allow both sum queries and updates in about log(n) time, which is much faster than recalculating sums from scratch. They are useful in situations like counting frequencies, managing cumulative data, or solving problems where data changes often and sums are needed frequently.
Fenwick trees provide fast updates and queries, ideal for dynamic cumulative data.
Real World Analogy

Imagine a library where books are grouped in sections, and each section keeps a note of how many books it has. To find out how many books are in the first few sections, you only need to check a few notes instead of counting every book. When a book is added or removed, only the notes for the affected sections are updated.

Structure of Fenwick Tree → Library sections holding notes about the number of books in their range
Prefix Sum Query → Checking a few section notes to quickly find the total number of books up to a point
Updating Values → Updating notes in all sections that include the changed book
Efficiency and Use Cases → Quickly knowing book counts even when books are added or removed often
Diagram
Diagram
Original array indices: 1  2  3  4  5  6  7  8
Fenwick tree array:    [0, 1, 3, 3, 10, 5, 11, 7, 36]

Index binary:          0001 0010 0011 0100 0101 0110 0111 1000

Partial sums cover:
 1: element 1
 2: elements 1-2
 3: element 3
 4: elements 1-4
 5: element 5
 6: elements 5-6
 7: element 7
 8: elements 1-8
This diagram shows how Fenwick tree array positions correspond to ranges of the original array using binary index patterns.
Key Facts
Fenwick treeA data structure that stores partial sums to allow fast prefix sum queries and updates.
Prefix sum queryFinding the sum of elements from the start of the list up to a given position.
Update operationChanging a value in the original list and updating the Fenwick tree accordingly.
Time complexityBoth queries and updates in a Fenwick tree take O(log n) time.
Binary index manipulationUsing the last set bit of indices to navigate and update the Fenwick tree efficiently.
Code Example
Data Structures Theory
class FenwickTree:
    def __init__(self, size):
        self.size = size
        self.tree = [0] * (size + 1)

    def update(self, index, delta):
        while index <= self.size:
            self.tree[index] += delta
            index += index & (-index)

    def prefix_sum(self, index):
        result = 0
        while index > 0:
            result += self.tree[index]
            index -= index & (-index)
        return result

# Example usage
arr = [1, 2, 0, 4, 1, 3, 2, 1]
fenw = FenwickTree(len(arr))
for i, val in enumerate(arr, 1):
    fenw.update(i, val)

print(fenw.prefix_sum(5))  # Sum of first 5 elements
fenw.update(3, 5)          # Increase element at index 3 by 5
print(fenw.prefix_sum(5))  # Sum after update
OutputSuccess
Common Confusions
Fenwick trees store the original array values directly.
Fenwick trees store the original array values directly. Fenwick trees store partial sums of ranges, not the original values themselves.
Fenwick trees can answer any range sum query directly.
Fenwick trees can answer any range sum query directly. Fenwick trees efficiently answer prefix sums; other range sums require combining prefix sums.
Updating a value requires recalculating the entire Fenwick tree.
Updating a value requires recalculating the entire Fenwick tree. Only a few positions in the Fenwick tree are updated using binary index jumps, not the entire tree.
Summary
Fenwick trees organize data to quickly find sums of the first part of a list and update values without recalculating everything.
They use binary index tricks to store and access overlapping partial sums efficiently.
Both querying sums and updating values happen in about log(n) time, making Fenwick trees useful for dynamic data.