二叉索引树(树状数组)入门

Posted beilili

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉索引树(树状数组)入门相关的知识,希望对你有一定的参考价值。

二叉索引树,即树状数组,被某神犇称之为是最漂亮的数据结构,所以蒟蒻北篱也去学习了一下传说中的树状数组。

限于蒟蒻北篱的语言表达能力太差(其实是懒),于是引用了度娘的一段对树状数组的解释

树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。

树状数组可以O(logn)的完成单点修改,单点查询和区间查询等操作,其实就是动态维护前缀和的过程,其中的所有操作都是以lowbit(x)操作为核心的。

lowbit

先来说一下lowbit(x)操作,lowbit(x)操作其实就是求x的二进制最低位(最低位代表的数字而不是位数),求x的lowbit也很简单,代码如下:

1 inline int lowbit(int x) {
2     return x&(-x);
3 }

其实就是利用了补码,证明很简单,这里略过。如果你不想写函数也可以宏定义:

1 #define lowbit(x) ((x)&(-(x)))

加多层括号是为了防止优先级错误,不必要时完全可以不加。

存储结构

树状数组看名字就知道只是一个数组,没什么好说的,但是,每个数组元素存储的不是原数组中的内容(这不是废话吗),而是原数组中(x-lowbit(x),x]内所有元素的和,而这就是我们查询时间为O(logn)的原因。

单点修改

如果要把数组中下标为x的元素加上t,那么学过数组的人肯定都会写:

1 inline void change(int x,int t) {
2     c[x]+=t;
3 }

这样就行了吗?不行。

我们在说存储结构的时候就说过了,树状数组中下标为元素的值等于原数组中(x-lowbit(x),x]内所有元素的和,所以我们修改树状数组的其他元素,我们假设树状数组有n个元素,于是代码如下:

1 inline void change(int x,int t) {
2     for(;x<=n;x+=lowbit(x)) {
3         c[x]+=t;
4     }
5 }

这样才是没有问题的修改操作。如果看懂了上面的存储原理想要理解的话也不难,理解不了就多看几遍图就懂了。

有没有发现我们并没有说建树的代码?那是因为直接把每一个点调用一次change操作就好了。

前缀和查询

我们利用树状数组可以做到O(logn)的前缀和查询,和单点修改类似的原理,这里不再多说,看代码就能懂的(笑

1 inline int sum(int x) {
2     int s=0;
3     for(;x;x-=lowbit(x)) {
4         s+=c[x];
5     }
6     return s;
7 }

区间查询

我们可以利用树状数组求[s,t]内所有元素的和,做法很简单,既然我们已经写出前缀和查询的代码,我们现在就要好好利用它,求出sum(t)和sum(s),然后两者相减就能得出答案,所以区间求和的时间也是O(logn)。

1 inline int query(int s,int t) {
2     return sum(t)-sum(s-1);
3 }

树状数组模板

最后丢一个模板好了(溜

 1 struct BIT{
 2     private:
 3         static const int maxn=5e5+10;
 4         int c[maxn],n;
 5     public:
 6         void init(int n) {
 7             memset(c,0,sizeof(c));
 8             this->n=n;
 9         }
10         inline void change(int x,int t) {
11             for(;x<=n;x+=lowbit(x)) {
12                 c[x]+=t;
13             }
14         }
15         inline int sum(int x) {
16             int s=0;
17             for(;x;x-=lowbit(x)) {
18                 s+=c[x];
19             }
20             return s;
21         }
22         inline int query(int s,int t) {
23             return sum(t)-sum(s-1);
24         }
25 };

 

以上是关于二叉索引树(树状数组)入门的主要内容,如果未能解决你的问题,请参考以下文章

二叉索引树 树状数组

浅析树状数组(二叉索引树)及一些模板

二叉索引树

C++ 树进阶系列之树状数组的树形之路

树状数组的基操

树状数组