树状数组是一种高效的存储方式,可以在nlogn时间内完成数据的更新与查询,下面给出树状数组的c++实现与使用。
首先,树状数组每一位存储的数据是原数组[x-lowbit(x)+1, x]上的总和,其中lowbit(x)是指x二进制最低1位对应的值,如(6)10 = (110)2,其二进制最低1位是第1位,故lowbit(6) = (10)2 = 2。其实现为:
int lowbit(int x) return x & (-x);
树状数组构建如下:
class FenwickTree{private:vector<int> tree;int n;int lowbit(int x) return x & (-x);public:// 下面是构建FenwickTree(int size){n = size;tree.resize(n+1, 0);} // 构建空的树状数组FenwickTree(const vector<int>& arr){n = arr.size();tree.resize(n+1, 0);for(int i = 0; i < n; i++) update(i+1, arr[i]);} // 依据已有的数组构建树状数组,,从1开始到第n位存储,第0位是0,效率为nlognvoid update(int i, int value){while(i <= n){tree[i] += value;i += lowbit(i);}}// 下面是查询与修改int query(int i){int sum = 0;while(i > 0){sum += tree[i];i -= lowbit(i);}return sum;} // 查询到第i位前缀和int rangeQuery(int l, int r){if(l > r) return 0; // [l, r]无定义return query(r) - query(l-1); // 差分} // 查询[l, r]上总和int get(int i){return query(i) - query(i-1); // 差分} // 查询原数组第i位的值int set(int i, int value){int oldValue = get(i);update(i, value - oldValue);}}
解释:
1.构建中,update函数会把所有包含arr[i]的tree对应位加上arr[i],其中通过i += lowbit(i)跳转到下一区间
2.查询中,通过i -= lowbit(i)跳回到上一个没有公共区间的相邻区间,从而获取i到开头的和
3.rangeQuery和get依据差分实现
4.set(i)中,会计算和原来值的差值,将差值加到所有包含arr[i]的tree相应位置上