文章目录
原理
用来解决数组修改与区间查询复杂度过高
对数组来说,暴力修改与查询的复杂度分别为O(1)
,O(n)
,总体复杂度为O(qn)
;若使用前缀和优化查询复杂度会变为O(1)
,修改的复杂度会变为O(n)
,总体复杂度仍为O(qn)
树状数组的修改与查询复杂度为O(logn)
,有效避免超时
详解
lowbit()函数
该函数的作用为: 求二进制数中最低位的1表示的十进制数
计算机中数值用补码进行存储,假设一数值为X,则-X可以通过
对X按位取反后 + 1
得到
let lowbit = function(X){
return X & -X;
}
将X与-X按位与,便可得到二进制中最低位的1。
树状数组
在A[]数组的基础上建立C[]数组,关系如下
C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]
C[7]=A[7]
C[6]=A[5]+A[6]
C[5]=A[5]
C[4]=A[1]+A[2]+A[3]+A[4]
C[3]=A[3]
C[2]=A[1]+A[2]
C[1]=A[1]
C[n]为后缀和,其长度与n所在的层数(自底向上数)相关
更新树状数组 可使用x += lowBit(x)
来更新C[n]数组
查询区间和 可使用x -= lowBit(x)
查找小于x所在的区间
查询[left,right]区间,可先求出[1,right]区间和,与[1,left]区间和,相减即可
模板题
1. LeetCode307. 区域和检索 - 数组可修改
//单点更新,区间求和
var NumArray = function(nums) {
this.tree = new Array(nums.length + 1).fill(0);
this.nums = nums;
for (let i = 0; i < nums.length; i++) {
this.add(i + 1, nums[i]);
//构建C[N]
}
};
NumArray.prototype.update = function(index, val) {
this.add(index + 1, val - this.nums[index]);
this.nums[index] = val;
};
NumArray.prototype.sumRange = function(left, right) {
return this.prefixSum(right + 1) - this.prefixSum(left);
};
NumArray.prototype.lowBit = function(x) {
return x & -x;
}
NumArray.prototype.add = function(index, val) {
while (index < this.tree.length) {
this.tree[index] += val;
index += this.lowBit(index);
}
}
NumArray.prototype.prefixSum = function(index) {
let sum = 0;
while (index > 0) {
sum += this.tree[index];
index -= this.lowBit(index);
}
return sum;
}
…待续
2. 区间更新,单点查询
3. 区间更新,区间查询