初识树状数组

树状数组:是一个查询和修改复杂度都为log(n)的数据结构,假设数组A[1..n],那么查询A[1]+...+A[n]的时,间是log级别的,而且是一个在线的数据结构。
树状数组原理如下:主要是依靠神奇的二进制转换进行保存数据。所以我们需要使用lowbit函数。

const int D = 1000005;
int s[D];
inline int lowbit(int x) {
return x & (-x);
}//用lowbit函数算出该数在二进制下的最后一个1的位置;

void add(int x, int w) {
while (x < D) {
s[x] += w;
x += lowbit(x);
}
}//修改树状数组中的元素:并且每次仅对x进行+lowbit的处理,如图所示,即修改了一个节点之后,只需要对在它的上层的全部节点进行修改,而不需要全程遍历修改,因而复杂度取决于二进制中的最大位数。
细节:如图,2节点存储了1节点的数据,即包含了2节点之前的全部数据和,4节点存储了3节点的数据和2节点的数据,即包含了4节点之前的全部数据和,6节点储存了5节点的数据,8节点储存了7节点和6节点的数据和4节点和2节点的数据,即代表了8节点之前的全部数据和。

int sum(int x) {
int ret = 0;
while (x > 0) {
ret += s[x];
x -= lowbit(x);
}
return ret;
}//计算前缀和的函数,为什么可以每次直接减少一个lowbit,证明如下:对于区间[x - lowbit + 1, x];
//设x为A1B,其中A为一窜二进制,B为B个0;
//那么,x - lowbit + 1就等于AB1,(当AB1不断进行+lowbit即可以得到A0B即x,此处先写出这个关系,方便后文阐述)即区间[AB1, A0B]之间的和可以只用A0B一个节点来表示,继续来看为什么,在add函数中,每当对该区间的任个数进行赋值或修改时,不断进行+lowbit最后都会对x即A1B进行赋值或修改,因此,x这个节点包含了这个区间内所有节点的修改结果,是最终结果的集合体。
因此,通过在add中进行赋值的顺序,我们可以看出,在sum函数中,要求前缀和,只需每次对该节点进行-lowbit即可,这样的复杂度仅仅取决于二进制中1的最多个数。
//另外还可以这样看:设原数组为A[1..N],树状数组为c[1..N],其中c[k] = A[k - lowbit + 1] + ... + A[k]。比如c[6] = A[5] + A[6]。

假设 A为被计数数组,C为树状数组(计数)

0000 0001:C1 = A1
0000 0010:C2 = A1 + A2
0000 0011:C3 = A3
0000 0100:C4 = A1 + A2 + A3 + A4
0000 0101:C5 = A5
0000 0110:C6 = A5 + A6
0000 0111:C7 = A7
0000 1000:C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
...
0001 0000:C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8+ A9 + A10 + A11 + A12 + A13 + A14 + A15+ A16

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值