树状数组小结

树状数组基本概念

    树状数组也是进行区间操作的常用数据结构。树状数组适用于单个元素经常修改,而且还反复求部分的区间和的情况.

    对于数组a,构造一个新的数组C,使得C[i] = a[i-2^k+1] + a[i-2^k+2] + ... + a[i]; 
(1) i >= 1; 
(2) k为i在二进制表示下末尾的连续的0的个数,2^k = i&(-i),通常用lowbit(i)表示i对应的2^k,lowbit(i) = 2^k = i&(-i); 
则数组C为数组a的树状数组。

数组数组的结构

    树状数组对应的结构如下图所示: 
树状数组
C[i] = a[i-lowbit(i)+1] + ... + a[i] 
C[1] = a[1] 
C[2] = a[1] + a[2] 
C[3] = a[3] 
C[4] = a[1] +a[2] + a[3] + a[4]

树状数组的操作

一、求连续区间的和

    树状数组可以快速的求数组a的一个连续区间的和: 
sum[k] = a[1] + a[2] + ... + a[k],则a[i] + a[i+1] + a[i+2] + ... +a[j] = sum[j] - sum[i-1] 
如果用C数组来表示即为: 
sum[k] = a[1] + a[2] + .. a[k] = C[N1] + C[N2] + ... +C[Nm] 
其中 Nm = k, Ni-1 = Ni - lowbit(Ni), N1 >=1

//查询
int Query(int p, int n){
	int sum = 0;
	while (p >= 1){
		sum += gC[p];
		p -= gLowBit[p];
	}
	return sum;
}

 

其中有log(n)个C元素,这就使得树状数组在连续区间求和时,可以达到log(n)的效率。 
证明略

二、单点更新

    对原始数组a中的一个元素ai进行更新,树状有且仅有如下几项会被更新:C[N1], C[N2], ... C[Nm] 
其中,N1 = i, Nk+1 = Nk + lowbit(Nk),且Nm <= N

//更新
void Update(int p, int n, bool add){
	while (p <= n){
		if (add){
			gC[p] ++;
		}
		else{
			gC[p] --;
		}
		p += gLowBit[p];
	}
}

 


树状数组和线段树

    树状数组的应用范围窄,树状数组适用于单个元素经常修改,而且还反复求部分的区间和的情况。所有能用树状数组解决的问题,均可以用线段树解决; 
    树状数组和线段树的时间复杂度均为O(nlogn),但是树状数组的常数小,效率略高;且树状数组的编程复杂度低。

posted @ 2015-09-06 18:04  农民伯伯-Coding  阅读(288)  评论(0编辑  收藏  举报