给定序列(数列)A,我们设一个数组C满足C[i] = A[i–2k+ 1] + … + A[i],其中,k为i在二进制下末尾0的个数,i从1开始算,则称C为树状数组(二进制索引树)。
对于给定的i,有三种求解方法:
(1) 2k= i&(i^(i-1))
(2) 2k= i^(i&(i-1))
(3) 2k= i & (-i) (补码性质)
快速地获取连续几个数的和、动态地修改一些位置上的数字。
注:如果我们用一个数组B[i]记录A中前i项和,查询的时间代价是O(1),但是在更新每一个元素时时间代价为O(n),而采用树状数组时间代价O(log2n)。
3.1 求树状数组C
方法一:根据公式C[i] = A[i–2k+ 1] + … + A[i]可以轻松的计算出数组C中的每一个元素。
方法二:初始化数组A与数组C为0,然后更新A中的每个元素为目标元素,即我们要用于查询部分和的数据。当更新A[i]时,采用自底向上的方式更新每一个包含A[i]的树状数组C的值。易得直接包含A[i]的为C[i],直接包含C[i]的为C[i+2k],其中k为i的二进制表示中0的个数,求解方法见定义。
方法一与方法二的时间复杂度分别为O(n2)与O(nlog2n),其中n为原始数据个数。
3.2 查询
我们将查询分为两部分:从起点开始的连续i个数的和与连续区间[k,m]之和。用sum[i]代表从起点开始连续i个数之和。
查询一:从起点开始的连续i个数
sum[i]包含了i项,而C[i]包含了从i开始向前数共2k项,因此我们有sum[i] = C[i] + sum[i-2k](sum[0] = 0)。而k代表了i的二进制表示中0的个数,i-2k表示将i中最后一个1置0,因此该递归式递归次数为i中1的个数,即log2i。可得查询操作的时间复杂度为log2n,其中n为原始数据个数。
注:i-2k =i&(i-1)
查询二:连续区间[k, m]数字之和S
因为S = sum[m] – sum[k-1],因此第二种查询可以转化为第一种查询。该计算方式进行了两次查询一,因此时间复杂度也为log2n。
3.3 修改元素的值
对元素的修改包括值的增加、减少与改变,而减少一个值可以理解为增加一个负值,改变也可以转变为增加一个值(或正或负),因此这里指描述增加即可。
3.3.1 增加
设对A[i]元素增加k:
(1) C[i] = C[i]+ k
(2) i = i+ 2k,如果i<n,执行(1);反之,运算结束。
该操作的时间复杂度为O(log2n)。
3.4 增加元素个数
相比于线段树来说,块状数组可以增加元素的个数,而不需要从新计算已经求出来的数据。
#include <iostream> #include <assert.h> using namespace std; #define MAXNUM 10 template<class T> class CTreeArry { public: CTreeArry(int num); ~CTreeArry(); private: int dataNum; public: T *sourceData; T *treeArry; void Init(const T arry[], int n); void Increase(const int i, const T value); void Reduce(const int i, const T value); void Change(const int i, const T value); T Query(const int end); T Query(const int start, const int end); void AddNumOfElem(const T arry[], int addNum); void upData(const int i, const T value); }; template<class T> CTreeArry<T>::CTreeArry(int num) :dataNum(num) { sourceData = new T[num+1]; treeArry = new T[num+1]; memset(sourceData, 0, sizeof(T)*(dataNum+1)); memset(treeArry, 0, sizeof(T)*(dataNum+1)); } template<class T> CTreeArry<T>::~CTreeArry() { delete []sourceData; delete []treeArry; } template<class T> void CTreeArry<T>::Init(const T arry[], int n) { for (int i=1; i<=n; ++i) { Increase(i, arry[i]); } } template<class T> void CTreeArry<T>::Increase(const int i, const T value) { assert(i>=1 && i<=dataNum); int temp = i; sourceData[temp] += value; for (; temp<=dataNum; temp += (temp&(-temp))) { treeArry[temp] += value; } } template<class T> void CTreeArry<T>::Reduce(const int i, const T value) { assert(i>=1 && i<=dataNum); Increase(i, -value); } template<class T> void CTreeArry<T>::Change(const int i, const T value) { assert(i>=1 && i<=dataNum); Increase(i, value-sourceData[i]); } template<class T> T CTreeArry<T>::Query(const int end) { assert(end>=1 && end<=dataNum); T sum = 0; //下面两个等价 // for (int i=end; i>0; i-=(i&(-i))) for (int i=end; i>0; i=i&(i-1)) { sum += treeArry[i]; } return sum; } template<class T> T CTreeArry<T>::Query(const int start, const int end) { assert(start>=1 && start<=end && end<=dataNum); return Query(end) - Query(start-1); } template<class T> void CTreeArry<T>::upData(const int i, const T value) { assert(i>=1 && i<=dataNum); for (int j=i; j<=dataNum; j+=(j&(-j))) { treeArry[j] += value; } } template<class T> void CTreeArry<T>::AddNumOfElem(const T arry[], int addNum) { int tempDataNum = dataNum; dataNum = dataNum + addNum; T *tempSource = sourceData; T *tempTreeArry = treeArry; sourceData = new T[dataNum + 1]; treeArry = new T[dataNum + 1]; memset(sourceData, 0, sizeof(T)*(dataNum + 1)); memset(treeArry, 0, sizeof(T)*(dataNum + 1)); for (int i=1; i<=tempDataNum; ++i) { sourceData[i] = tempSource[i]; if (i+(i&(-i))<=tempDataNum) { treeArry[i] = tempTreeArry[i]; } else { upData(i, tempTreeArry[i]); } } delete tempSource; delete tempTreeArry; for (int i=tempDataNum+1; i<=dataNum; ++i) { Increase(i, arry[i-tempDataNum-1]); } }
原文:http://blog.csdn.net/woniu317/article/details/24356473