树状数组初步
2015-09-12 17:24
274 查看
树状数组(Binary Indexed Tree(BIT), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可 以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值,且其常数之小是线段树无法做到的。
如图,其中a数组保存了初始读入的n个值,c数组即为我们构建的树状数组。
那么容易发现:
C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
由此,我们成功存下了2^n前缀和,可以轻松在o(1)时间内求解该前缀和。
如果前缀长不是2^n形式,我们又该怎么做呢?
首先介绍一个函数——lowbit,代码如下:
为什么要用这样一个函数呢?事实上经过测试我们发现,对于1-n内某数x,lowbit(x)表示其“管辖”着从它向后共2^lowbit(x)个数的和,而通过这种方法我们可以在不超过logn的时间内求出所有的lowbit加和,得到的就是该长度下的前缀和。
不难证明,对于1-n内的两个数i,j,满足i<=j,sum(i,j)=sum(1,j)-sum(1,i-1);
故对于任意区间,我们都可以用这种方法求解该区间内所有值的和,代码贴上
理解代码后建议自己打一遍,代码细节很多比较容易错
那么对于这样一棵树,它是怎么被构造出来的呢?
事实上同样利用了我们的lowbit函数,通过确定自己的“管辖范围”求出该点下的权值。
继续贴代码(这段比较容易懂,就不多说了)
当然,对于这样一个神奇的数据结构我们必须了解它的局限性,即——无法进行区间修改(神犇别跟我说什么可以的话,一旦区间修改就只能单点查询一般用不到好的不送= =)
而单点修改也并非仅修改原来的a数组,而是同时修改你所用的“带有区间性质”的数组
单点修改分两种:
1)将一个点的值增加或减少某个值
方法:将其值修改后,用lowbit求出其父节点及之上的节点
直接贴代码,如果lowbit理解深入一定能看懂
2)讲一个点的值改成另一个值
方法:很简单,求出修改后值和修改前值的差即可,再一次变成方法一修改233
代码贴一个
就是这样,以上所有的代码都可以直接使用,加上变量和主程序就是一个完整的代码。
以上就是本人对于树状数组的一点小心得,如果有问题加我qq:1064864324,记得加备注。
喜欢就收藏一下,记得关注订阅哦亲(卖萌中~~)
如图,其中a数组保存了初始读入的n个值,c数组即为我们构建的树状数组。
那么容易发现:
C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
由此,我们成功存下了2^n前缀和,可以轻松在o(1)时间内求解该前缀和。
如果前缀长不是2^n形式,我们又该怎么做呢?
首先介绍一个函数——lowbit,代码如下:
function lowbit(x:longint):longint; begin exit(x and (-x)); end;
为什么要用这样一个函数呢?事实上经过测试我们发现,对于1-n内某数x,lowbit(x)表示其“管辖”着从它向后共2^lowbit(x)个数的和,而通过这种方法我们可以在不超过logn的时间内求出所有的lowbit加和,得到的就是该长度下的前缀和。
不难证明,对于1-n内的两个数i,j,满足i<=j,sum(i,j)=sum(1,j)-sum(1,i-1);
故对于任意区间,我们都可以用这种方法求解该区间内所有值的和,代码贴上
function sum(l,r:longint):longint; var s1,s2:longint; ans1,ans2:longint; begin s1:=l-1; s2:=r; ans1:=0; ans2:=0; while s1>0 do begin inc(ans1,bit[s1]); dec(s1,lowbit(s1)); end; while s2>0 do begin inc(ans2,bit[s2]); dec(s2,lowbit(s2)); end; exit(ans2-ans1); end;
理解代码后建议自己打一遍,代码细节很多比较容易错
那么对于这样一棵树,它是怎么被构造出来的呢?
事实上同样利用了我们的lowbit函数,通过确定自己的“管辖范围”求出该点下的权值。
继续贴代码(这段比较容易懂,就不多说了)
procedure in_in(pos,x:longint); begin while pos<=n do begin inc(bit[pos],x); inc(pos,lowbit(pos)); end; end;
当然,对于这样一个神奇的数据结构我们必须了解它的局限性,即——无法进行区间修改(神犇别跟我说什么可以的话,一旦区间修改就只能单点查询一般用不到好的不送= =)
而单点修改也并非仅修改原来的a数组,而是同时修改你所用的“带有区间性质”的数组
单点修改分两种:
1)将一个点的值增加或减少某个值
方法:将其值修改后,用lowbit求出其父节点及之上的节点
直接贴代码,如果lowbit理解深入一定能看懂
procedure change2(x,y:longint); begin while x<=n do begin inc(bit[x],y); inc(x,lowbit(x)); end; end;
2)讲一个点的值改成另一个值
方法:很简单,求出修改后值和修改前值的差即可,再一次变成方法一修改233
代码贴一个
procedure change1(x,y:longint); var ans1,ans2:longint; s1,s2:longint; begin s1:=y-a[x]; s2:=x; while s2<=n do begin inc(bit[s2],s1); inc(s2,lowbit(s2)); end; end;
就是这样,以上所有的代码都可以直接使用,加上变量和主程序就是一个完整的代码。
以上就是本人对于树状数组的一点小心得,如果有问题加我qq:1064864324,记得加备注。
喜欢就收藏一下,记得关注订阅哦亲(卖萌中~~)
相关文章推荐
- 第八章
- 自己封装的AJAX (带JSON)
- 烂泥:vcenter通过模板部署vm
- python学习——collections模块
- Windows环境下使用VS2010编译OpenSSL
- 树莓派上手常见问题处理[转]
- linux Read-only file system
- 2404 Permutation Recovery【模拟】
- HDU 1203 I NEED A OFFER!【01背包】
- Palindrome Partitioning
- JAVA中使用哈希表
- php多种实例理解无限极分类
- Kali+Win7双系统
- php取得json前面有乱码
- UVALive 3902 Network
- Word Ladder II
- Surrounded Regions
- Java Web 中文乱码问题总结
- 在Eclipse或者ADT中使用ButterKnifeZelezny,Android组件初始化从此变得简单易懂!!!!
- 蓝懿iOS培训日志10 个人随笔(部分总结)