一分钟了解树状数组
-
树状数组的功能:
在程序设计中,经常遇到求区间和(query)并且更新区间的某个数值(update)的问题。
如果不使用任何算法,update的时间复杂度为O(1)但是query的时间复杂度是O(n)
如果使用前缀和的算法,update的时间复杂度是O(n),query的时间复杂度是O(1)
也就是说,以上两种算法无论如何都会有一个操作的时间复杂度到达O(n)级别,如果两种操作都要进行多次,消耗时间就会过长。
如果想要兼顾两种操作的时间复杂度,就需要使用树状数组或者线段树的算法,在这里主要介绍树状数组。
树状数组的query和update的时间复杂度均为O(logn) -
树状数组的原理
树状数组的精髓是lowbit()函数,其主要功能是返回某个数二进制最低位上的1的权值。
例如:
lowbit(1)=1;
lowbit(2)=2;
lowbit(3)=1;
lowbit(4)=4;
以下是c代码:
int lowbit(int x){
return x&-x;
}
或者
#define lowbit(x) -x&x
树状数组中的每个值都等于原数组中前lowbit()项的和。
如下图:
- 树状数组的创建:
//a为原数组,c为树状数组,下同
int a[100100];
int c[100100];
int n;
//a为原数组,c为树状数组,下同
void creat(){
for( int i=1;i<=n;i++){
for( int j=0;j<lowbit(i);j++){
c[i]+=a[i-j];
}
}
}
- 树状数组的query
树状数组每个数都是该元素前lowbit()项元素的和(包含该元素本身)
int query( int x){
int sum=0;
while(x>0){
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
求区间和只需要
c[r]-c[l-1]
- 树状数组的update
树状数组的update需要修改树状数组中所有包含下标i的数组元素。
而从i开始,第一个包含下标i的元素就是i+lowbit(i)
void update( int i,int val){
while(i<=n){
c[i]+=val;
i+=lowbit(i);
}
}