数据结构 | 树状数组
树状数组 (Binary Index Tree, BIT / Fenwick Tree): 对于满足结合律且可差分的问题,可在
满足结合律且可差分的问题:对于运算
树状数组
时间复杂度:
单点修改和区间查询均为该复杂度。
分析
意义
首先我们来看两种数组,一种是普通数组,长度为
对于上面这个普通数组,单点修改时间复杂度
我们可以用前缀和优化,变成长度为
对于上面这个前缀和数组,区间求和的时间复杂度为
可以发现以上两种数组都有长处和短处,但根据木桶效应,要想整体优秀,最好还是各方面平衡。树状数组便是平衡了这两种操作的时间复杂度的数据结构,它将其平均为了
原理及代码
普通数组单点修改快,是因为每一个元素都是独立的。前缀和数组区间求和快,是因为它的元素就是区间的和。为了平衡,我们可以将其变为一个个独立小区间,这样单点修改时只需要修改小区间,无需整个修改。区间求和时可以直接用小区间求和,也可以少算很多步。这时小区间的排布非常重要,如何才能达到最好的效果。
区间排布
树状数组的区间排布用的是二进制思想,就其英文名“二进制下标树”也可以知道这点。其排布如下图所示:
Array 为原数组,BIT 为转化为的树状数组,数组下标均从
这么说还是不够形象,举几个例子:
用通俗易懂的话来说:
第
const int MAXN = 1e6;
int Fenwick_Tree[MAXN];
// 如果不是全局变量记得初始化为0
区间求和
例如我们要求区间
此时我们需要用到
我们知道正数的原码等于补码,那么一个数和自己的相反数做按位与,便是原码和补码做按位与,即可计算出
// 查询(0,pos]的区间和
int query(int pos)
{
int ans = 0;
for (int i = pos; i; i -= i & -i)
ans += Fenwick_Tree[i];
return ans;
}
以上计算的是从
// 查询[l,r]的区间和
inline int query(int l, int r)
{
return query(r) - query(l - 1);
}
单点修改
例如我们要将第
// 第pos位加上val
void update(int pos, int val)
{
for (int i = pos; i < MAXN; i += i & -i)
Fenwick_Tree[i] += val;
}
本文采用 CC BY-SA 4.0 许可,本文 Markdown 源码:Haotian-BiJi
好详细,收藏一波。
嘿嘿,你是本站第一个评论~