树状数组
树状数组
一.为什么需要树状数组?
举一个简单的例子,假设有一个数组
我们需要实现两个功能:
1.计算数组任意X~X+N项元素的和
2.能够动态的增删改节点
有两种解决方法:
1.将数组直接存储下来
此时,求X~X+N的和时间复杂度是O(N)
修改并维护数组的时间复杂度是O(1)
2.使用前缀和数组
此时,求X~X+N的和时间复杂度是O(1)
修改并维护数组的时间复杂度是O(N)
以上两种方法都是各有缺陷,树状数组能够将这两个操作都维持在O(logn)的时间复杂度

二、Lowbit函数
lowbit是指一个整数的二进制表示,从后往前第一个1所代表的大小
例如 3 = 011 那么3的lowbit为1
6 =0110 6的lowbit为10 = 2
lowbit有一个很简单的求法: X & -X
树状数组的所有操作都是基于lowbit()函数
二.算法模拟
3.1 插入或更新元素
当我们需要插入或更新某一位置的元素时,需要从这个位置开始,依次更新到树根中所有元素
设数组总长度为8,初始状态全为0
以下标为2的元素加5为例,初始化pos = 2
要时刻保证 pos <= length
1.A[2] + 5 0+5=5
2.pos += lowbit(2) pos = 4
3.A[4] + 5 0+5=5
4.pos += lowbit(4) pos = 8
5.A[8] + 5 0+5=5
6.8号已到达数组末尾,更新结束
添加后
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|
| 0 | 5 | 0 | 5 | 0 | 0 | 0 | 5 |
3.2 查询
查询0~pos之间的和
以求A[3] - A[5]的值为例,先求出A[0]到A[3]的和sum1,在求出A[0]到A[5]的值sum2,最后sum2-sum1即为结果
以求A[0]到A[5]的和为例,初始化ans=0,pos = 5
1.ans += A[pos] pos = 5 ans = 0
2.pos -= lowbit(5) pos = 4 ans = 5
3.ans += A[pos] pos = 4 ans = 5
4.pos -= lowbit(5) pos = 0 ans = 5
sum1 = 5
三. 代码
void lowbit(int x)
{
return x & (-x);
}
void add(int pos, int val)
{
for(; pos < n; pos += lowbit(pos))
val += tree[pos];
}
int query(int pos)
{
int val = 0;
for(; pos; pos -= lowbit(pos)) val += tree[pos];
return val
}

浙公网安备 33010602011771号