我搜遍了网络,只在topcoder的网站上了解到树状数组这个结构是在设计压缩算法时被发现的。这个数据结构真是天才的构想,膜拜!
树状数组的基础是一个被构造出来的式子:C[i]=A[i]+A[i-1]+....+A[i-2^k+1];k代表i的二进制的最后连续0的个数 比如 对于1000和101000,k=3。至于这个式子是怎么被构造出来的,k为什么要代表这个。因为二进制的思想。
根据这个图来看节点与其子树的关系
接下来则很容易发现 节点和子节点的是有关系的,这种关系就是 i=j+lowbit(j); lowbit是j的最低位1所代表的数字 比如对于 1000(8的二进制) 1000=100+lowbit(100)=110+lowbit(110)=111+lowbit(111);
这个关系是树状数组的核心,有了这个关系,我们可以把子区间的变化以log2n的次数传递上去
那我们也知道了 当我们要求 1-n的和时,我们同样把n表示为2进制,我们知道 C[i]=A[i]+A[i-1]+....+A[i-2^k+1]; 所以对于i 是不是我们只要把他所有的1都用上 就可以表示1-n的和?
举个例子 求1-11000 则 Sum(11000)=C[11000]+C[10000]; 因为 根据C[i]的构造方法 C[i]是从A[i]开始的2^k个元素的和,则C[11000]求了A[11000] A[10111] A[10110] A[10101] A[10100] A[10010] A[10001] 这2^k个数 然后接着C[10000]求出了剩下 10000个元素的和 到了这我们就大概了解了树状数组的发明者的天才的构造是从何而来的了 普通的求1-n的和储存的数据太多,而这位天才则想,我们能不能根据二进制的思想来储存这些值呢?任何一个数,都可以由若干个二进制数相加而成,如果我们在求Sum(n)之前就知道了 n对应的二进制数从最低位开始,每个1所代表的数字的前2^i个数的和,我们不就能在时间复杂度log2(n)内求出所有的值 比如 101110 我们如果知道 101110->101101 101100->101001 101000->100001 100000->1各自的和,就可以在空间复杂度和时间复杂度很小的情况下求出1-101110了
然后对于区间和的修改 又利用每个小区间向上转移 修改了大区间的和
总的来说 树状数组就是利用了二进制的思想求和 写的有点乱 但只要你看懂了 二进制思想的那部分 相信看懂和实现树状数组并不难