树状数组
需求
- 随机修改数组中一个数字
- 求前缀和
- 以上操作需要频繁操作
预备知识:lowbit
lowbit指数字的二进制最低位1及后续0取出后的数字 例如:10:1
2
3int lowbit(int a){
return a & -a;
}1010 & 0110 = 0010
,即10的lowbit为2。
x | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
lowbit(x) | 1 | 2 | 1 | 4 | 1 | 2 | 1 | 8 | 1 | 2 |
注意:0不存在lowbit,所以数组的下标需要右偏移一位(使用时偏移或初始化多1位)
树状数组
- tarr从1开始,arr从0开始
- 树状数组的每一个元素
tarr[i]
:其值为a[j]+...a[i-1]+a[i-1]
,其中连加的项总共lowbit(i)
个 - 在树状数组中,为什么需要寻找其父节点?有两个用处:1、初始化树状数组。2、修改某个数同时修改其祖上一系列树状数组的值。对于
tarr[i]
而言,其直接父节点即为tarr[i + lowbit(i)]
,通过这种方式逐步向上迭代,即可探寻祖上一条链 - 对于给定数组:
arr = [1,3,2,6,4,1]
,可得其对应的树状数组:tarr = [1, 1+3, 2, 1+3+2+6, 4, 4+1] -> [1, 4, 2, 12, 4, 5]
- 求和时,例如
sum(idx=13)
- 先得到
res += tarr[13]
,随后idx -= lowbit(idx)
,此时idx==12
- 再得到
res += tarr[12]
,随后idx -= lowbit(12)
,此时idx==8
- 再得到
res += tarr[8]
,随后idx -= lowbit(8)
,此时idx==0
结束循环 - 最终,
res = tarr[13] + tarr[12] + tarr[8] = (arr[12]) + (arr[8]+...+arr[11]) + (arr[0]+...+arr[7])
- 先得到
- 总之,对于树状数组
tarr
idx += lowbit(idx)
是为了寻找祖上,修改和初始化用到idx -= lowbit(idx)
是为了求和
代码
1 | class TreeArr { |