Open In App

Implementation of compressed 2D Binary Indexed tree

A compressed 2D Binary Indexed Tree (BIT) is a data structure that is used to efficiently store and query two-dimensional arrays, particularly when the values in the array are frequently updated.

The compressed 2D Binary Indexed Tree (BIT) is a data structure used for fast querying and updating values in a two-dimensional array. It is an extension of the Binary Indexed Tree that is used for one-dimensional arrays. In the compressed 2D BIT, each node in the binary tree represents the sum of a rectangular subregion of the two-dimensional array. This allows for efficient range queries and updates on the two-dimensional array.



To save memory, the compressed 2D BIT uses a compression technique that removes empty nodes from the binary tree. This results in a smaller tree and a smaller memory footprint, which makes it useful for applications where memory is limited. The compressed 2D BIT can be used in a variety of applications, including image processing, spatial data analysis, and computer graphics.

The implementation of 2D BIT consists of the following components:

To implement a compressed 2D Binary Indexed Tree, we can follow the following steps:



Below is the Python code to implement the above steps:




#include <bits/stdc++.h>
using namespace std;
 
// Compressed2DBIT class
class Compressed2DBIT {
    int n, m;
    vector<vector<int> > bit, tree;
 
public:
    // Constructor
    Compressed2DBIT(int n, int m)
    {
        this->n = n;
        this->m = m;
 
        // Initialize bit and tree vectors
        bit.assign(n + 1, vector<int>(m + 1, 0));
        tree.assign(n, vector<int>(m, 0));
    }
 
    // Update function for bit vector
    void update(int x, int y, int val)
    {
        while (x <= n) {
            int y1 = y;
            while (y1 <= m) {
                bit[x][y1] += val;
                y1 += y1 & -y1;
            }
            x += x & -x;
        }
    }
 
    // Query function for bit vector
    int query(int x, int y)
    {
        int s = 0;
        while (x > 0) {
            int y1 = y;
            while (y1 > 0) {
                s += bit[x][y1];
                y1 -= y1 & -y1;
            }
            x -= x & -x;
        }
        return s;
    }
 
    // Compresses the bit vector into a 2D tree
    void compress()
    {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                // Compute the sum of elements in the
                // corresponding rectangle
                tree[i - 1][j - 1] = query(i, j)
                                     - query(i - 1, j)
                                     - query(i, j - 1)
                                     + query(i - 1, j - 1);
            }
        }
        bit.clear(); // Clear bit vector after compression
    }
 
    // Getter function for compressed tree
    vector<vector<int> > get_compressed_tree()
    {
        return tree;
    }
};
 
// Main function
int main()
{
    // Initialize input matrix
    vector<vector<int> > arr = { { 1, 2, 3, 4, 5 },
                                 { 6, 7, 8, 9, 10 },
                                 { 11, 12, 13, 14, 15 },
                                 { 16, 17, 18, 19, 20 } };
 
    // Create Compressed2DBIT object and update with input
    // matrix
    Compressed2DBIT bit(4, 5);
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 5; j++) {
            bit.update(i + 1, j + 1, arr[i][j]);
        }
    }
 
    // Test query function
    cout << bit.query(2, 3) << endl; // expected output: 27
    cout << bit.query(4, 5) << endl; // expected output: 210
 
    // Compress the bit vector and test query function again
    bit.compress();
    cout << bit.query(2, 3) << endl; // expected output: 27
    cout << bit.query(4, 5) << endl; // expected output: 210
 
    // Get compressed tree and print it
    vector<vector<int> > compressed_tree
        = bit.get_compressed_tree();
    for (auto row : compressed_tree) {
        for (auto x : row) {
            cout << x << " ";
        }
        cout << endl;
    }
 
    return 0;
}




import java.util.*;
 
// Compressed2DBIT class
class Compressed2DBIT {
    int n, m;
    List<List<Integer>> bit, tree;
 
    // Constructor
    public Compressed2DBIT(int n, int m) {
        this.n = n;
        this.m = m;
 
        // Initialize bit and tree lists
        bit = new ArrayList<>(n + 1);
        for (int i = 0; i <= n; i++) {
            bit.add(new ArrayList<>(m + 1));
            for (int j = 0; j <= m; j++) {
                bit.get(i).add(0);
            }
        }
 
        tree = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            tree.add(new ArrayList<>(m));
            for (int j = 0; j < m; j++) {
                tree.get(i).add(0);
            }
        }
    }
 
    // Update function for bit list
    public void update(int x, int y, int val) {
        while (x <= n) {
            int y1 = y;
            while (y1 <= m) {
                bit.get(x).set(y1, bit.get(x).get(y1) + val);
                y1 += y1 & -y1;
            }
            x += x & -x;
        }
    }
 
    // Query function for bit list
    public int query(int x, int y) {
        int s = 0;
        while (x > 0) {
            int y1 = y;
            while (y1 > 0) {
                s += bit.get(x).get(y1);
                y1 -= y1 & -y1;
            }
            x -= x & -x;
        }
        return s;
    }
 
    // Compresses the bit list into a 2D tree
    public void compress() {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                // Compute the sum of elements in the
                // corresponding rectangle
                tree.get(i - 1).set(j - 1, query(i, j)
                                     - query(i - 1, j)
                                     - query(i, j - 1)
                                     + query(i - 1, j - 1));
            }
        }
        bit.clear(); // Clear bit list after compression
    }
 
    // Getter function for compressed tree
    public List<List<Integer>> getCompressedTree() {
        return tree;
    }
}
 
// Main function
public class Main {
    public static void main(String[] args) {
        // Initialize input matrix
        List<List<Integer>> arr = new ArrayList<>(Arrays.asList(
            Arrays.asList(1, 2, 3, 4, 5),
            Arrays.asList(6, 7, 8, 9, 10),
            Arrays.asList(11, 12, 13, 14, 15),
            Arrays.asList(16, 17, 18, 19, 20)
        ));
 
        // Create Compressed2DBIT object and update with input
        // matrix
        Compressed2DBIT bit = new Compressed2DBIT(4, 5);
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 5; j++) {
                bit.update(i + 1, j + 1, arr.get(i).get(j));
            }
        }
      // Compress the bit list into a 2D tree
bit.compress();
          // Get the compressed tree and print it
    List<List<Integer>> compressedTree = bit.getCompressedTree();
    for (int i = 0; i < compressedTree.size(); i++) {
        for (int j = 0; j < compressedTree.get(i).size(); j++) {
            System.out.print(compressedTree.get(i).get(j) + " ");
        }
        System.out.println();
    }
}
}
       




# Define a class named Compressed2DBIT
class Compressed2DBIT:
 
  # Define the constructor
  # with arguments n and m
    def __init__(self, n, m):
        self.n = n
        self.m = m
 
        # Create a 2D BIT (Binary Indexed
        # Tree) with zeros, with dimensions
        # n + 1 and m + 1 initialize 2D
        # BIT with zeros
        self.bit = [[0] * (m + 1) for _ in range(n + 1)]
        self.tree = None
 
    def update(self, x, y, val):
        """
        Update the value at (x, y) in the 2D array by adding `val` to it.
        """
 
        # Update the values in the BIT
        # based on the provided x, y,
        # and val
        while x <= self.n:
            y1 = y
            while y1 <= self.m:
                self.bit[x][y1] += val
 
                # Compute the next y value
                # to update based on the
                # current y value
                y1 += y1 & -y1
 
                # Compute the next x value
                # to update based on the
                # current x value
            x += x & -x
 
    def query(self, x, y):
        """
        Query the sum of the elements in
        the subarray from (1, 1) to (x, y)
        """
        s = 0
        while x > 0:
            y1 = y
            while y1 > 0:
                s += self.bit[x][y1]
                y1 -= y1 & -y1
            x -= x & -x
        return s
 
    def compress(self):
        """
        Compress the 2D array using the
        Fenwick tree (Binary Indexed Tree)
        technique.
        """
 
        # initialize compressed 2D array
        # with zeros
        self.tree = [
            [0] * self.m for _ in range(self.n)]
        for i in range(1, self.n + 1):
            for j in range(1, self.m + 1):
 
                # Calculate the sum of the
                # elements in the subarray
                # from (1, 1) to (i, j)
                # using the formula:
                # sum(x1, y1, x2, y2) =
                # sum(x2, y2) - sum(x1-1, y2)
                # - sum(x2, y1-1) +
                # sum(x1-1, y1-1)
                self.tree[i-1][j-1] = self.query(i, j) - self.query(
                    i-1, j) - self.query(i, j-1) + self.query(i-1, j-1)
 
                # set the 2D BIT to None
                # to save memory
        self.bit_compressed = None
 
 
# Example usage
arr = [[1, 2, 3, 4, 5],
       [6, 7, 8, 9, 10],
       [11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20]]
 
bit = Compressed2DBIT(4, 5)
for i in range(4):
    for j in range(5):
        bit.update(i + 1, j + 1, arr[i][j])
 
print(bit.query(2, 3))  # expected output: 27
print(bit.query(4, 5))  # expected output: 210
 
bit.compress()
print(bit.query(2, 3))  # expected output: 27
print(bit.query(4, 5))  # expected output: 210
# expected output:
# [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10],
# [11, 12, 13, 14, 15],
# [16, 17, 18, 19, 20]]
print(bit.tree)




using System;
using System.Collections.Generic;
 
class Program {
  // Main  function
  static void Main(string[] args)
  {
    // Initialize input matrix
    List<List<int> > arr = new List<List<int> >() {
      new List<int>() { 1, 2, 3, 4, 5 },
      new List<int>() { 6, 7, 8, 9, 10 },
      new List<int>() { 11, 12, 13, 14, 15 },
      new List<int>() { 16, 17, 18, 19, 20 }
    };
 
    // Create Compressed2DBIT object and update with
    // input matrix
    Compressed2DBIT bit = new Compressed2DBIT(4, 5);
    for (int i = 0; i < 4; i++) {
      for (int j = 0; j < 5; j++) {
        bit.update(i + 1, j + 1, arr[i][j]);
      }
    }
 
    // Compress the bit list into a 2D tree
    bit.compress();
 
    // Get the compressed tree and print it
    List<List<int> > compressedTree
      = bit.getCompressedTree();
    for (int i = 0; i < compressedTree.Count; i++) {
      for (int j = 0; j < compressedTree[i].Count;
           j++) {
        Console.Write(compressedTree[i][j] + " ");
      }
      Console.WriteLine();
    }
  }
}
 
// Compressed2DBIT class
class Compressed2DBIT {
  int n, m;
  List<List<int> > bit, tree;
 
  // Constructor
  public Compressed2DBIT(int n, int m)
  {
    this.n = n;
    this.m = m;
 
    // Initialize bit and tree lists
    bit = new List<List<int> >();
    for (int i = 0; i <= n; i++) {
      bit.Add(new List<int>());
      for (int j = 0; j <= m; j++) {
        bit[i].Add(0);
      }
    }
 
    tree = new List<List<int> >();
    for (int i = 0; i < n; i++) {
      tree.Add(new List<int>());
      for (int j = 0; j < m; j++) {
        tree[i].Add(0);
      }
    }
  }
 
  // Update function for bit list
  public void update(int x, int y, int val)
  {
    while (x <= n) {
      int y1 = y;
      while (y1 <= m) {
        bit[x][y1] += val;
        y1 += y1 & -y1;
      }
      x += x & -x;
    }
  }
 
  // Query function for bit list
  public int query(int x, int y)
  {
    int s = 0;
    while (x > 0) {
      int y1 = y;
      while (y1 > 0) {
        s += bit[x][y1];
        y1 -= y1 & -y1;
      }
      x -= x & -x;
    }
    return s;
  }
 
  // Compresses the bit list into a 2D tree
  public void compress()
  {
    for (int i = 1; i <= n; i++) {
      for (int j = 1; j <= m; j++) {
        // Compute the sum of elements in the
        // corresponding rectangle
        tree[i - 1][j - 1] = query(i, j)
          - query(i - 1, j)
          - query(i, j - 1)
          + query(i - 1, j - 1);
      }
    }
    bit.Clear(); // Clear bit list after compression
  }
 
  // Getter function for compressed tree
  public List<List<int> > getCompressedTree()
  {
    return tree;
  }
}
 
// This code is contributed by Tapesh(tapeshdua420)




class Compressed2DBIT {
    constructor(n, m) {
        this.n = n;
        this.m = m;
 
        // Initialize bit and tree arrays
        this.bit = new Array(n + 1);
        this.tree = new Array(n);
 
        for (let i = 0; i <= n; i++) {
            this.bit[i] = new Array(m + 1).fill(0);
        }
 
        for (let i = 0; i < n; i++) {
            this.tree[i] = new Array(m).fill(0);
        }
    }
 
    // Update function for bit array
    update(x, y, val) {
        while (x <= this.n) {
            let y1 = y;
            while (y1 <= this.m) {
                this.bit[x][y1] += val;
                y1 += y1 & -y1;
            }
            x += x & -x;
        }
    }
 
    // Query function for bit array
    query(x, y) {
        let s = 0;
        while (x > 0) {
            let y1 = y;
            while (y1 > 0) {
                s += this.bit[x][y1];
                y1 -= y1 & -y1;
            }
            x -= x & -x;
        }
        return s;
    }
 
    // Compresses the bit array into a 2D tree
    compress() {
        for (let i = 1; i <= this.n; i++) {
            for (let j = 1; j <= this.m; j++) {
                // Compute the sum of elements in the corresponding rectangle
                this.tree[i - 1][j - 1] = this.query(i, j) - this.query(i - 1, j) - this.query(i, j - 1) + this.query(i - 1, j - 1);
            }
        }
    }
 
    // Getter function for compressed tree
    getCompressedTree() {
        return this.tree;
    }
}
 
// Initialize input matrix
const arr = [
    [1, 2, 3, 4, 5],
    [6, 7, 8, 9, 10],
    [11, 12, 13, 14, 15],
    [16, 17, 18, 19, 20]
];
 
// Create Compressed2DBIT object and update with input matrix
const bit = new Compressed2DBIT(4, 5);
for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 5; j++) {
        bit.update(i + 1, j + 1, arr[i][j]);
    }
}
 
// Test query function
console.log(bit.query(2, 3)); // expected output: 27
console.log(bit.query(4, 5)); // expected output: 210
 
// Compress the bit array and test query function again
bit.compress();
console.log(bit.query(2, 3)); // expected output: 27
console.log(bit.query(4, 5)); // expected output: 210
 
// Get compressed tree and print it
const compressedTree = bit.getCompressedTree();
for (const row of compressedTree) {
    console.log(row.join(" "));
}

Output
27
210
27
210
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]]

Time Complexity: 

Auxiliary Space:

Advantages of compressed 2D Binary Indexed tree:

Disadvantages of compressed 2D Binary Indexed tree:


Article Tags :