您的位置:首页 > 编程语言 > C语言/C++

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

2016-02-15 20:13 405 查看
一、树状数组的用处

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

二、树状数组的建立

假设输入数组为

vector<int> nums


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

size = nums.size();
bitTree = vector<int>(size+1,0);
for (int i =1;i<=size;i++)
{
int k=i;
while (k<size+1)
{
bitTree[k] += nums[i-1];
k += k & -k;
}
}


树状数组建立的原理:

如下图,来源自百度图片,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,即可求得结果。



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

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

void update(int i, int val) {
int ret = val-nums[i];
int k=i+1;
while (k<size+1)
{
bitTree[k]+=ret;
k+=k&-k;
}
nums[i] = val;
}


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

int sumRange(int i, int j) {
int result1 = 0,result2 = 0;
int k=i;
while (k)
{
result1+=sum[k];
k-=k&-k;
}
k=j+1;
while (k)
{
result2+=sum[k];
k -= k&-k;
}
return result2-result1;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: