1. Description
A Fenwick Tree is a binary indexed tree that allows efficient:
- do range queries from [0, m] like (sum, max, min) in O(log n)
- do point updates in O(log n)
The meaning of tree[node] represents the answer for the range covered by the left most binary bit.
Space used is n+1, Fenwick Tree is using the left most binary bit of node index to represent the range. For example: node = 1 = 0001 in bin, the length of range covered by this node is 1 node = 4 = 0100 in bin, the lenght of range covered by this node is 4 node = 6 = 0110 in bin, the length of range covered by this node is 2
The query the range [0, 6], we have to query node=6 and node=4
Youtube: Fenwick Tree (Binary Index Tree) Quick Tutorial and Source Code Explanation
The Fenwick tree use the left most bit a lot, the followings are some tricks
i & -i will give the left most binary bit i - (i & -i) will remove the left most binary bit
2. code
2.1. template using fenwick tree to find the sum in range [0, node]
vector<int> arr(n); vector<int> tree(n+1) // tree node start at index 1 not 0 // Fenwick Tree: update (0 based), increase node n and populate the effect to root void update(vector<int>& tree, int node, int incr) { node ++; //populate to the parents while(node < tree.size()) { tree[node] += incr; node += (node & -node); // go to the parent by adding the left most binary bit } return; } // Fenwick Tree: query the range [0, i] (0 based) for the sum int query(vector<int>& tree, int i) { int ans = 0; i ++; // loop through each segment while(i>0) { ans += tree[i]; i -= (i & -i); } return ans; } // key idea, add the current node to it's immediate parent void build(vector<int>& tree, vector<int>& arr){ copy(arr.begin(), arr.end(), tree.begin()+1); for(int i=1; i<tree.size(); i++) { int p = i + (i & -i); // get the immediate parent if(p < tree.size()) { tree[p] += tree[i]; // add to the parent } } return; }
2.2. [limitation] template using fenwick tree to find the max element in range [0, node]
Fenwick tree has some limitation when find the min or max element in range [0, node]. It does not allow updates.
Ex: update(1, 10) then update(1, 2) will make the tree wrong. In a max-Fenwick Tree (like the one above), each bit[i] stores the maximum of some range. When you do: tree[i] = std::max(tree[i], val); …it only increases values. If you try to decrease (i.e., update a value at index i to a smaller one), the tree does not fix the max values in ancestors — so stale max values remain in the tree.
vector<int> arr(n); vector<int> tree(n+1); // update node value to val and populate the change void update(vector<int>& tree, int node, int val) { // tree is one based node ++; // tree[node] gives the max element in current range, if current range, if the update val is smaller or equal // this applies to its parent while(node < tree.size()) { // we can stop early if tree[node] >= val if(tree[node] >= val) { return ; } // now tree[node] < val tree[node] = val; // go to the next parent node node += (node & -node); } return ; } // return the max element in the range [0, node] int query(vector<int>& tree, int node) { node ++; int ans = INT_MIN; while(node > 0) { ans = max(ans, tree[node]); // go to ajcent node node -= (node & -node); } return ans; }