假定原始数组为x[i], 树状数组为BIT[i].
1. 求任意前缀和,单点更新的时间复杂度为O(lgn) (n为数组大小)
2. 树状数组第i个元素BIT[i]保存了2^L 个数组x元素的和.L为 i的二进制表达式里从右数第一个1的位置(位置从0开始).比如如果 i的二进制为10000100,那么L = 2, BIT[i]的值等于2^2=4个x数组元素的值.
并且是从 x[[i]开始到往前数2^L个x数组的元素之和:
下面是索引从1到16的BIT数组元素对应的x数组元素和映射表:
从上面的图可以看出,x数组的任何一个元素在BIT数组里出现的次数都是新数组大小对数级的.
3.单点更新
假如x[5]的值变了,我们需要更新 BIT[5], BIT[6],BIT[8], 和 BIT[16]; 5加1(5的二进制右数第一个1的位置为0, 2^0 = 1)等于6;6加上2(6的二进制右数第一个1的位置为1, 2^1 = 2)等于8;
8加上8(8的二进制右数第一个1的位置为 3, 2^3 =8)等于16.这样就遍历了所有需要更新的树状数组.
我们定义一个函数 lowestOneBit(int i),它返回值是 2^L, L为输入的二进制右数第一个1的位置.
int lowestOneBit(int i)
{
return i & ( -i );
}
这个实现利用了补码的思想(负数的补码等于原码取反加1).
另一种实现:
int lowestOneBit( int i )
{
return i - i & ( i - 1 );
}
那么,单点更新的函数实现如下:
void update( int index, int delta )
{
int i;
for( i = index; i < ARRAY_SIZE; ++i)
{
BIT[i] += delta;
i += lowestOneBit(i);
}
}
4. 区间求和:
求 x数组1到i的和(前缀和):
int getRangedValue(int end )
{
int ret = 0;
int i = end;
while( i > 0 )
{
ret += BIT[i];
i = i - lowestOneBit(i);
}
return ret;
}
如果是求任意区间的和,可以求两次前缀和,然后相减得到.
参考:
http://www.cs.ucf.edu/~dmarino/progcontests/cop4516/notes/BIT-Notes.pdf