1 定义
树状数组不是树,是数组。
树状数组不是树,是数组。
树状数组不是树,是数组。
按照名字去猜含义,树状数组是像树一样的数组,的确如此。更精确地描述如下文。
1.1 前置知识 - lowbit函数
记一个正整数x的二进制表示为
其中等于1的位为
那么x可以二进制分解成
我们设其中 依据此,我们将区间 可以分成以下个区间
对于上述每个区间,记区间右端点是,那么区间长度为的二进制分解下最小的2次幂。 结合例子来看,对于数字7,他的二进制分解为
那么区间就可以分解成
即三个区间。
这个给出的lowbit函数就是可以快速计算出x的最小二次幂的一个函数。
实现如下:
int LowBit(int x)
{
return x & -x;
}
1.2 树状数组
树状数组就是基于上述切割数组的一种数据结构,主要作用是维护原数组的前缀和。
记原数组为, 对应构建的树状数组为,对于每个, 有:
即表示区间的A数组的和。 结合具体例子来看
对于树状数组,有如下性质:
-
- 存放着所有以为根节点的子树中所有叶子节点的和
-
- 的中子节点个数为
lowbit(x)
- 的中子节点个数为
-
- 除跟节点外,每个节点的父节点是
-
- 树的深度是
O(logN)
- 树的深度是
2 实现
树状数组主要支持两个操作,分别是
- 1 单点增加: 单点增加原始数组A的一个数,同时可以维护树状数组B;
- 2 查询前缀和: 查询原始数组前多少项数之和。 其中单点增加可用来初始化树状数组。
2.1 单点增加
由上述分析可知,对于, 只有以及其父节点存着,那么变化时,只要递归向上更新即可。
// N is array length
// a[x] b[x]
void Add(int x, int y)
{
int idx = x;
while (idx <= N) {
b[idx] += y;
idx += LowBit(idx); // 父节点
}
}
初始化树状数组就是将全部初始化0,每一位进行Add操作即可。
2.2 查询前缀和
存放的就是的和,递归向下求解即可。
int GetPreSum(int x)
{
int sum = 0;
int idx = x;
while (idx >= 1) {
sum += b[idx];
idx -= LowBit(x);
}
return sum;
}
2.3 全部模板代码
const int N = 100010; // for example
int a[N];
int b[N];
int LowBit(int x)
{
return x & -x;
}
void Add(int x, int y)
{
int idx = x;
while (idx <= N) {
b[idx] += y;
idx += LowBit(idx);
}
}
int GetPreSum(int x)
{
int sum = 0;
int idx = x;
while (idx >= 1) {
sum += b[idx];
idx -= LowBit(x);
}
return sum;
}