树状树组(Binary Indexed Tree (BIT))的C++部分实现

一、树状数组的用处

树状树组是将一个线性数组保存为“树状”,当修改某点的值、求某个区间的和的时候能够有效的减少时间复杂度。当数组长度为N,实时对数组进行M次修改或求和,最坏的情况下复杂度是O(M*N)。

二、树状数组的建立

假设输入数组为

vector<int> nums

将其转化为树状数组的本质在于将数组的原先顺序打乱后,经过特殊的求和方法,组合成新的数组,代码如下。关键点在于k+=k&-k,这是一个利用二进制码的特点完成树状数组下标的选取。

 1 size = nums.size();
 2 bitTree = vector<int>(size+1,0);
 3 for (int i =1;i<=size;i++)
 4 {
 5        int k=i;
 6        while (k<size+1)
 7        {
 8               bitTree[k] += nums[i-1];
 9               k += k & -k;
10         }
11  }

树状数组建立的原理:

如下图,来源自百度图片,c数组是树状数组,a数组是原先的线性数组,可以看见树状数组的元素是a数组的元素或者元素之和,其中c[8]+c[9]即是线性数组的a的总和。对于树状数组而言,其建立顺序(按下标排列)是(循环1)c[1]+=a[0];c[2]+=a[0];c[4]+=a[0];c[8]+=a[0];

                  (循环2)c[2]+=a[1];c[4]+=a[1];c[8]+=a[1];

                  (循环3)c[3]+=a[2];c[4]+=a[2];c[8]+=a[2];

                  (循环4)c[4]+=a[3];c[8]+=a[3];

                  (循环5)c[5]+=a[4];c[8]+=a[4];

                  (循环6)c[6]+=a[5];c[8]+=a[5];

                  (循环7)c[7]+=a[6];c[8]+=a[6];

                  (循环8)c[8]+=a[7];

                  (循环9)c[9]+=a[8];

可见,第一次循环加的是1,2,4,8;第二次是2,4,8;第三次是3,4,8等等。其规律是第i循环中,c[k]与a[i-1]相加,而k与i的关系是k是i二进制数保留最高位1后相加的结果,比如i=2=0010,其二进制数保留最高位1后是0010,故第1个k=0010+0010=0100=4,再对k进行处理,得第二个k=8,直至k>size+1。又比如i=3=0010,其二进制数保留最高位1后是0010,第1个k=0010+0010=0100,第2个k=0100+0100=8。之所以采用这种方法因为树状数组采用了二分的思想,比如c[8]会等于a[0]~a[3]与a[4]~a[7]两部分的和。

故建立数组的关键在于求i二进制数保留最高位1后相加的结果,其方法是:令k=i,k+=k&-k,即可求得结果。

三、树状数组的更新和部分求和

更新数组:如果此时原数组中的一个元素被改变,那么树状数组中许多值需要被更新,这因为树状数组中的元素之间存在可能的联系,这种联系与树状数组下标值相关。因此更新树状数组并不是单纯的单个值替换,代码如下:

 1     void update(int i, int val) {
 2         int ret = val-nums[i];
 3         int k=i+1;
 4         while (k<size+1)
 5         {
 6             bitTree[k]+=ret;
 7             k+=k&-k;
 8         }
 9         nums[i] = val;
10     }

数组部分求和:分别先求0到i下标的和,再求0到j+1下标的和,它们之间的差即是下标i到下标j的和,数组部分求和代码如下:

 1     int sumRange(int i, int j) {
 2         int result1 = 0,result2 = 0;
 3         int k=i;
 4         while (k)
 5         {
 6             result1+=sum[k];
 7             k-=k&-k;
 8         }
 9         k=j+1;
10         while (k)
11         {
12             result2+=sum[k];
13             k -= k&-k;
14         }
15         return result2-result1;
16     }
原文地址:https://www.cnblogs.com/ycloneal/p/5191256.html