Data Structure and Algorithm
Data Structure
Binary Indexed Tree
Introduction to BIT

Binary Indexed Tree (Fenwick Tree)

A Binary Indexed Tree (BIT), also known as a Fenwick Tree, is a data structure that can efficiently update elements and calculate prefix sums in an array of numbers.

Compared to a Segment Tree, a Binary Indexed Tree requires less memory (it uses an array of size N + 1 instead of 4 * N) and is much easier to implement.


Operations and Bitwise Logic

In a BIT, we represent the tree structure using a 1-based indexed array BITree. The parent/child relationship is determined by the binary representation of the indices.

The Bitwise Trick: i & (-i)

This operation isolates the last set bit (the lowest significant bit with value 1) of the integer i.

  • Example: For i = 12 (binary 1100), 12 & (-12) yields 4 (binary 0100).

1. Retrieving Prefix Sum (getSum)

To get the sum of elements from the beginning of the array up to index i:

  1. Initialize sum = 0.
  2. Add BITree[i] to sum.
  3. Move to the parent node by subtracting the last set bit from i: i = i - (i & -i)
  4. Repeat until i becomes 0.

2. Updating an Element (update)

When we update an element at index i by adding a value val, we must update all ancestors of i in the tree:

  1. Add val to BITree[i].
  2. Move to the next node to update by adding the last set bit to i: i = i + (i & -i)
  3. Repeat until i exceeds the size of the array N.

JavaScript Implementation

class BinaryIndexedTree {
    constructor(arr) {
        this.n = arr.length;
        this.BITree = new Array(this.n + 1).fill(0);
 
        // Construct the BITree by inserting values one by one
        for (let i = 0; i < this.n; i++) {
            this.update(i, arr[i]);
        }
    }
 
    // Update the BIT at a specific index by a given value
    update(index, val) {
        // Transform to 1-based indexing for the BITree
        let i = index + 1;
 
        // Traverse all ancestors and add val
        while (i <= this.n) {
            this.BITree[i] += val;
 
            // Move index to parent/next node
            i += i & -i;
        }
    }
 
    // Helper to get prefix sum of arr[0..index]
    getSum(index) {
        let sum = 0;
        let i = index + 1; // 1-based indexing
 
        // Traverse ancestors of index
        while (i > 0) {
            sum += this.BITree[i];
 
            // Move index to parent node (clear the last set bit)
            i -= i & -i;
        }
        return sum;
    }
 
    // Get range sum in [L, R]
    getRangeSum(L, R) {
        if (L === 0) return this.getSum(R);
        return this.getSum(R) - this.getSum(L - 1);
    }
}
 
// Driver Code
const arr = [2, 1, 1, 3, 2, 3, 4, 5, 6, 7, 8, 9];
const bit = new BinaryIndexedTree(arr);
 
// Get sum of first 5 elements (indices 0 to 4: 2 + 1 + 1 + 3 + 2 = 9)
console.log("Sum of elements in arr[0..4]:", bit.getSum(4));
 
// Update index 3 by adding 6 (arr[3] becomes 3 + 6 = 9)
bit.update(3, 6);
 
// Get sum of first 5 elements again (2 + 1 + 1 + 9 + 2 = 15)
console.log("Sum in arr[0..4] after update:", bit.getSum(4));

Complexity Analysis

OperationTime ComplexityAuxiliary Space
ConstructionO(N log N)O(N)
Query (getSum)O(log N)O(1)
UpdateO(log N)O(1)
Space Complexity-O(N)