场景:一个数组 有两个操作
1. 修改某个元素数值
2.求前n个数/区间数值和
如果我们对数组元素两两求和,如图
然而 图中没用的数字 有很多
可以发现 每一层的第偶数个数字都是没用的(在计算和时 直接用它正上方的数字就好)
如计算前5个数字和时,只需要把前四个数字和19加上第五个数字5即可
观察发现剩下的数据正好有n个,我们可以把这些数据装进一个数组中,这个数组正好和原数组一样长,也就是树状数组。
每一个元素都对应着一段区间和 求和时 我们只需要找到对应的区间即可。
修改数据时也只需要修改包含该数据的几个区间即可。
题目代码如下:
class NumArray {
int[] tree;
int lowbit(int x) {
return x & -x;
}
int query(int x) {
int ans = 0;
for (int i = x; i > 0; i -= lowbit(i)) {
ans += tree[i];
}
return ans;
}
void add(int x, int u) {
for (int i = x; i <= n; i += lowbit(i)) {
tree[i] += u;
}
}
int[] nums;
int n;
public NumArray(int[] _nums) {
nums = _nums;
n = nums.length;
tree = new int[n + 1];
for (int i = 0; i < n; i++) {
add(i + 1, nums[i]);
}
}
public void update(int i, int val) {
add(i + 1, val - nums[i]);
nums[i] = val;
}
public int sumRange(int l, int r) {
return query(r + 1) - query(l);
}
}
lowbit函数 x&(-x)的结果是:
1.x为奇数 结果为1
2.x为偶数 结果为能整除这个偶数的最大的2的幂, 即: m = x & -x , 则 x % m = 0, 且 m = 2^k
性质:
1.对于b[i]来说,它所对应的区间长度为 lowbit(i)
2.计算前i个元素的和sum(i)时,sum(i)=sum(i - lowbit(i)) + b[i] (性质1可得b[i]所代表的区间长度为lowbit(i))
3.一个序列正上方的序列正好是b[i + lowbit(i)],所以我们在修改某个值的时候 只需要不断向上加lowbit(i)就可以了