Golang program to implement a binary indexed tree (Fenwick tree)


Binary Indexed Tree (also Fenwick Tree) is a data structure that efficiently handles range queries and point updates on an array. In this article, we are going to explore two different methods to implement a binary indexed tree in go, here implementation means we are performing main operations of a binary indexed tree that are creating a BIT(initialization), updation and prefix sum query.

Explanation

Fenwick Tree is a binary tree structure which efficiently maintains cumulative information about an array, specifically prefix sums and allows faster updates and queries on ranges. It has applications in various algorithms and problems like finding cumulative sums and frequency counts.

Original Array:     1 2 3 4 5 6 7 8

Fenwick Tree:       1 3  3  10  5  11  7  36
                      |  |      |     |   |
                      1  2      4     8   16

The values in the Fenwick tree is obtained by:

  • The first index contains the commutative sum of the single element 1 in the original array.

  • The second index contains the cumulative sum of elements at index 1 and 2 that is 1+2 =3.

  • The third index contain the cumulative sum of index element 3 that is 3.

  • The fourth index contain the cumulative sum of elements at index 1,2,3, and 4 in original array that is 1+2+3+4 =10.

  • Now the fifth index is the cumulative sum of single element 5 in original array that is 5.

  • The sixth index is commutative sum of element of index 5 and 6 that is 11

  • Index 7 contain the cumulative sum of single element at index 7 that is 7.

  • Index 8 contain the cumulative sum of all the elements in the original array that are 1+2+3+4+5+6+7+8 = 36.

Syntax

func NewTrie() *Trie func NewBinaryIndexedTree(size int) *BinaryIndexedTree

The syntax defines a function named NewBinaryIndexedTree which is a constructor function for the BinaryIndexedTree (Fenwick Tree) data structure, used to create and initialize a new BinaryIndexedTree instance with the specified size.

func (bit *BinaryIndexedTree) updateUtil(index, delta int)

The syntax defines a function named updateUtil which updates the cumulative sum at specific indices in the implementation of the Binary Indexed Tree (Fenwick Tree) data structure, represented as an array.

Algorithm

  • Start by initializing the Fenwick Tree with zeros, with the array size increased by one.

  • To update, traverse the Fenwick Tree adding values to specific positions and their corresponding ancestors.

  • For prefix sum, traverse the Fenwick Tree subtracting values from specific positions and their ancestors.

  • For a single element query, calculate the prefix sum up to the required index.

  • For a range query, calculate the sum of prefixes for the given range.

Example 1

In this example, we implement a binary indexed tree in go using BinaryIndexedTree struct which encompasses the tree's size and array representation. NewBinaryIndexedTree function initializes the tree while accommodating 1-based indexing. The update method increments tree nodes along the tree's path, while tquery method calculates the prefix sum efficiently.

package main
import "fmt"
type BinaryIndexedTree struct {
    n   int  
    bit []int 
}
func NewBinaryIndexedTree(size int) *BinaryIndexedTree {
    return &BinaryIndexedTree{
    	n:   size + 1, 
    	bit: make([]int, size+1),
	}
}
func (bit *BinaryIndexedTree) update(index, delta int) {
	for i := index; i < bit.n; i += i & -i {
     	bit.bit[i] += delta
	}
}
func (bit *BinaryIndexedTree) query(index int) int {
    sum := 0
	for i := index; i > 0; i -= i & -i {
    	sum += bit.bit[i]
    }
    return sum
}
func main() {
	arr := []int{1, 2, 3, 4, 5}
    size := len(arr)
    bit := NewBinaryIndexedTree(size)
	for i, val := range arr {
    	bit.update(i+1, val)
    }
    fmt.Println("Original Array:", arr)
	fmt.Println("Prefix sum from index 1 to 4 (Method 1):", bit.query(4))
	bit.update(3, 6)
	fmt.Println("Updated Array:", bit.bit[1:])
	fmt.Println("Prefix sum from index 1 to 4 (Method 1):", bit.query(4))
}

Output

Original Array: [1 2 3 4 5]
Prefix sum from index 1 to 4 (Method 1): 10
Updated Array: [1 3 9 16 5]
Prefix sum from index 1 to 4 (Method 1): 16

Example 2

In this example,we will implement a binary indexed tree in go using BinaryIndexedTree struct encapsulating tree's size and an array representation. NewBinaryIndexedTree function initializes the tree while considering 1-based indexing. Using auxiliary recursive functions for tree updates and prefix sum calculations, updateUtil function modifies nodes along the tree's path, while queryUtil calculates prefix sums.

package main
import "fmt"
type BinaryIndexedTree struct {
	n   int   
	bit []int 
}
func NewBinaryIndexedTree(size int) *BinaryIndexedTree {
    return &BinaryIndexedTree{
     	n:   size + 1, 
    	bit: make([]int, size+1),
    }
}
func (bit *BinaryIndexedTree) update(index, delta int) {
    bit.updateUtil(index, delta)
}
func (bit *BinaryIndexedTree) updateUtil(index, delta int) {
	if index <= 0 || index >= bit.n {
    	return
    }
    bit.bit[index] += delta
    bit.updateUtil(index+(index&-index), delta)
}
func (bit *BinaryIndexedTree) query(index int) int {
    return bit.queryUtil(index)
}
func (bit *BinaryIndexedTree) queryUtil(index int) int {
	if index <= 0 {
    	return 0
	}
	return bit.bit[index] + bit.queryUtil(index-(index&-index))
}
func main() {
	arr := []int{1, 2, 3, 4, 5}
	size := len(arr)
	bit := NewBinaryIndexedTree(size)
    for i, val := range arr {
    	bit.update(i+1, val)
    }
	fmt.Println("Original Array:", arr)
	fmt.Println("Prefix sum from index 1 to 4 (Method 2):", bit.query(4))
	bit.update(3, 6)
	fmt.Println("Updated Array:", bit.bit[1:])
	fmt.Println("Prefix sum from index 1 to 4 (Method 2):", bit.query(4))
}

Output

Original Array: [1 2 3 4 5]
Prefix sum from index 1 to 4 (Method 2): 10
Updated Array: [1 3 9 16 5]
Prefix sum from index 1 to 4 (Method 2): 16

Real Life Implementation

  • Traffic Management: The usage of Fenwick trees in traffic management systems allows for the structuring and retrieval of data relevant to vehicle movements within defined zones, hence assisting in the optimization of traffic monitoring and analysis operations.

  • Sensor Data Analysis: Fenwick trees in Internet of Things (IoT) applications allow for more effective sensor data storage and analysis. This involves calculating critical metrics such as average temperature and humidity measurements within specified time periods.

Conclusion

Fenwick Tree is a binary tree structure which efficiently maintains cumulative information about an array. In this article, we have looked at two different examples to implement a binary indexed tree in go, offering efficient operations for cumulative frequency queries on arrays. The first example uses an array for ease of implementation and memory efficiency, making it a preferred choice for practical use. On the other hand, Method 2 provides a recursive insight into the tree-like structure of the Binary Indexed Tree, proving to be valuable for educational purposes.

Updated on: 18-Oct-2023

59 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements