Unraveling the Magic of Fenwick Trees - A Beginner's Guide
2023/11/13 Tree Fenwick

Fenwick Trees, also known as Binary Indexed Trees (BIT), are an elegant and efficient data structure used to perform range queries and updates on an array. Originally designed by Peter Fenwick in 1994, these trees have found applications in various fields, ranging from computational geometry to competitive programming. In this blog post, we’ll explore the fundamentals of Fenwick Trees, understand their construction, and delve into practical examples.

Understanding Fenwick Trees

At its core, a Fenwick Tree is designed to provide a fast and memory-efficient solution for cumulative frequency queries on an array. It excels at answering questions like “What is the sum of elements from index 1 to i?” efficiently.

Basic Concepts

1. Binary Indexed Tree Structure:

  • A Fenwick Tree is represented as an array BIT[], where each element BIT[i] stores the cumulative sum of a specific range.

2. Construction:

  • The construction of a Fenwick Tree involves updating each element based on its contribution to different ranges.

3. Query Operation:

  • Fenwick Trees efficiently answer range sum queries using a recursive and bitwise approach.

4. Update Operation:

  • Updating an element in a Fenwick Tree involves modifying all the affected ranges efficiently.

Constructing a Fenwick Tree

Let’s walk through the step-by-step construction of a Fenwick Tree for an array arr[].

  1. Initialize the Fenwick Tree:
    BIT = [0] * (len(arr) + 1)
    
  2. Update Function:
    def update(index, value):
        while index <= len(arr):
            BIT[index] += value
            index += index & -index
    
  3. Construction:
    def construct_fenwick_tree(arr):
        for i in range(1, len(arr) + 1):
            update(i, arr[i - 1])
    
  4. Query Function:
    def query(index):
        result = 0
        while index > 0:
            result += BIT[index]
            index -= index & -index
        return result
    

Practical Example

Let’s consider an array arr = [3, 2, -1, 6, 5, 4, -3, 3, 7, 2, 3] and construct a Fenwick Tree for it.

# Constructing the Fenwick Tree
arr = [3, 2, -1, 6, 5, 4, -3, 3, 7, 2, 3]
BIT = [0] * (len(arr) + 1)

def update(index, value):
    while index <= len(arr):
        BIT[index] += value
        index += index & -index

def construct_fenwick_tree(arr):
    for i in range(1, len(arr) + 1):
        update(i, arr[i - 1])

# Querying the sum of elements from index 1 to 6
result = query(6) - query(0)
print(result)  # Output: 19

In this example, we construct a Fenwick Tree for the given array and then query the sum of elements from index 1 to 6 efficiently.

Conclusion

Fenwick Trees offer a powerful and versatile solution for range queries and updates. Their simplicity and efficiency make them valuable in scenarios where cumulative frequency operations are frequent. As you explore and apply Fenwick Trees, you’ll uncover their ability to optimize various algorithms and computations. Happy coding!



Thank you for reading this article 😊

I'd love to hear your thoughts & feedback in the comment section below!


If you liked it, please share it with your friends and help this blog to grow!