树状数组
树状数组是前缀和在数值更新方向的一种进化:
假设给出一个数组a,b是该数组的前缀和,c是该数组的树状数组。
先给出树状数组的图解:
为什么要创建树状数组呢?我们知道,在前缀和b中,如果想要改变a的某一个位置元素的值,会对前缀和数组造成极大的影响,最坏的情况下一个元素的改变会对整个b数组产生影响,更新b数组的时间复杂度为O(n);但是对于树状结构的数组,就会降为O(log(n))的级别。下面展示如何构建树状数组:
可以看到,对于奇数(树状数组一般以1为起始坐标)位置的元素就是对应原数组的元素,对于偶数位置的元素有如下规律:对于位置i来说,假设其可以整除2的k次方却无法整除2的k+1次方,那么c[i] = a[i] + a[i -1] + a[i - 2] + …… + a[i - 2^k + 1],一共2的k次方个元素,其实这个规律对于奇数也满足。当然,从上图我们可以看出,我们可以利用已经计算出的树状数组去计算后面的树状数组:c[i] = a[i] + c[i - 1] + c[i - 2] + c[i - 4] +……+ c[i - 2^k]。
先给出一个算法:lowbit算法
int lowbit(int x)
{
return x & -x;
}
这个算法有什么用呢?可以计算出x最大因数K,其中K是2的整数次幂,在上面的递推公式中,我们显然用到了2的一些幂次因此,可以用该算法去计算。
给出计算树状数组c的代码
void creat_c(int pos)
{
c[pos] = a[pos];
for(int i = 1; i < lowbit(pos); i += lowbit(i))
c[pos] += c[pos - i];
}
创建完之后就是使用了,该数组基本用法有两种:
1.单点更新,就是更新a数组的某个值后,可以通过O(log(n))的时间复杂度去更新c数组
2.区间计算,单点更新的目的还是为了去计算区间和
下面给出代码
void point_update(int pos, int value)//pos位置加上value
{
for(int i = pos; i <= n; i += lowbit(i))
c[i] += value;
}
int interval_clac(int left, int right)//计算[left,right]之间元素的和
{
int res = 0;
for(int i = pos2; i; i -= lowbit(i))
res += c[i];
for(int i = pos1 - 1; i; i -= lowbit(j))
res -= c[i];
return res;
}
至此,树状数组的基本操作就结束了。

浙公网安备 33010602011771号