树状数组详解

 

 一、树状数组是干什么的?

       树状数组或者二叉索引树也称作Binary Indexed Tree,又叫做Fenwick树;它的查询和修改的时间复杂度都是log(n),空间复杂度则为O(n),这是因为树状数组通过将线性结构转化成树状结构,从而进行跳跃式扫描。通常使用在高效的计算数列的前缀和区间和

二、树状数组怎么干的?

树状数组详解_第1张图片

 

在图中,a数组就是原数组,c数组则是树状数组,我们不难发现

C1 = A1
C2 = A1+A2
C3 = A3
C4 = A1+A2+A3+A4
C5 = A5
C6 = A5+A6
C7 = A7
C8 = A1+A2+A3+A4+A5+A6+A7+A8

       为了发现这些公式的规律,我们要先了解lowbit(k)函数,它就是把k的二进制的高位1全部清空,只留下最低位的1,例如10的二进制是1010,则lowbit(k)=lowbit(1010)=0010(2进制),用代码实现为:lowbit(k)=k&-k,这是位运算,我们知道一个数加一个负号是把这个数的二进制取反+1,如-10的二进制就是-1010=0101+1=0110,然后用1010&0110,答案就是0010了!

       lowbit是用来联系a数组和c数组的!c[k]表示从a[k]开始往左连续求lowbit(k)个数的和,比如c[0110]=a[0110]+a[0101],就是从110开始计算了2(lowbit(0110)=0010)个数的和。可以看到其实只有低位的1起作用(lowbit(k)函数的作用)。可以显然的得出c[0010]=a[0010]+a[0001],注意0010这个数,除了高位其余位都是0,这时他本身本身就是lowbit,即k=lowbit(k)=k&-k。

        既然关系建立好了,那么如何实现A某一个位置数据更改?其实A数组不会直接改的,它每次更改其实都要通过维护C数组的性质来实现的,通常后面的求和要用到。比如我们更改了a[0011],c3 = a3,c4 = a1+a2+a3+a4,c8 = a1+a2+a3+a4+a5+a6+a7+a8,从关系式中不难看出。c[0011],c0100],c[1000]会随着a[0011]的改变而改变。(在程序中,数据的更改是由c数组体现的,而c数组的改变是由我们对c数组进行操作来实现的)。c改变的系数与a的系数,它们之间有什么必然联系或规律吗?仔细观察0011——>0100——>1000的变化,0011+0001=0100,1000=0100+0100,其满足关系式

k=k+lowbit(k)。

      不必考虑为什么非要展开成这种关系, 我们只需注意:c的构成性质(分组性质):它决定了c[0011]只会直接影响c[0100];而c[0100]只会直接影响c[1000],单向影响的关系恰好满足k +=lowbit(k),下面就是更新维护树的代码:

void add(int k,int num)
{
	   while(k<=n)
	{
		tree[k]+=num;
		k+=k&-k;
	}
}

     区间和, 比如求a数组0001~0110的和,c4 = a1+a2+a3+a4,c6 = a5+a6。就是c[0100]+c[0110]。

int read(int k)//1~k的区间和
{
	int sum=0;
	while(k)
	{
		sum+=tree[k];	
		k-=k&-k;
	}
	return sum;
}

三、总结一下吧

          树状数组是按照二分对数组进行分组;维护和查询都是O(lgn)的复杂度,复杂度取决于最坏的情况,也是O(lgn);lowbit这里只是一个技巧,关键在于明白C数组的构成规律。

你可能感兴趣的:(树状数组,树状数组详解,树状数组)