关于“树状数组”
来源:互联网 发布:商之翼yii2源码 编辑:程序博客网 时间:2024/06/05 01:10
线段树有的时候是比较复杂且没有必要的,于是就会造成很大的时间复杂度和空间复杂度。于是,树状数组是线段树在某些情况下很大的优化。
首先先代入一张数状数组的图。
(注:此图乃是转载之)
图中将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
到了c8停止我们可以看到8分为可以分为1-4,5-8两个部分,而这两个部分又可以分为1-2与3-4和5-6和7-8,在其他情况下也亦是这样。
那么很容易可以看出,树状数组是利用二分的思想来的。
那么接下来就是很巧妙的地方。
有一个东西two,那么two(k)表示的意思是将k的后缀到第一个1位置的数(二进制情况下),也就是说在k的二进制情况下,保留后面的0和最后一个1.
具体怎么做呢?
Two(k)=k&(-k)
举一个例子:
E.g:
当k为10是
(10)10=(1010)2
(-10)10=(0110)2 //显而易见,一个正数的二进制情况下的负数形式是正数的二进制取反加一。
那么
1010
&0110
----------
0010
也就是(10)2
对于一个树状数组c,c[k]表示从a[k]开始往左two(k)个数的和,这个在图中很容易可以看出来。
那么怎么对于一个树状数组进行更改呢?
这也用到了two这个东西,是的,特别巧妙。
再在图中找一个样例吧。
就比如我们当前要把a3更改为5。
那么我们就必须更改c3,c4和c8,这一点在图中也可以看出来。
那么(3)10=(0011)2
(4)10=(0100)2
(8)10=(1000)2
可以在二进制中发现,在二进制中,每一次都往高位改1
那么就可以借助two继续修改,也就是设当前这个位置在k,那么下一个位置就是在k+two(k)。将例子带进来看一下。
(0011)2+(0011)2&(-0011)2
=(0011)2+(0011)2&(1101)2
=(0011)2+(0001)2
=(0100)2
=(4)10
下一个就不举例了,是不是很神奇?
那为什么会这么神奇呢?主要是因为树状数组的二分的分组性质吧,这么奇妙的东西我只能抽象化的理解,就比如一个c,他的父亲肯定包含他,他的爷爷肯定也包含他,以此类推。
那么再说说求和的问题。
前面说过,c[k]表示从a[k]开始往左two(k)个数的和。
比如求a2到a4的和
那么就可以将将1到4的和找出来,再将1到2-1的和找出来,相减,很显然是一个前缀和的做法,就不必多说了。
那么接下来就看一下树状数组是怎么实现的。
修改:
void xiugai(int v,int p){ while (p<=n) { c[p]=c[p]+v; p=p+two(p); }}
求和
int qiuhe(int z){ int sum1=0; while (z!=0) { sum1=sum1+c[z]; z=z-two(z); } return sum1;}
关于two很简单
int two(int x){ return (x&(-x));}
这就是最基本的树状数组,应根据不同的题中不同情况来决定如何使用。
难点:
1,如何理解树状数组的思想(√)
2,two的具体作用(×)
最后,将树状数组的思想稍微抽象化一点,从二进制来考虑树状数组的奇妙应该不难了(即使我现在还是没有完全弄懂)
题目:
1,求和(√)
2,星星点灯(√)
3,指纹(×)
- 关于树状数组
- 关于树状数组
- 关于“树状数组”
- 关于树状数组
- 关于树状数组的姿势
- 关于树状数组的一个小问题
- 关于树状数组求逆序数
- 关于树状数组一些有意思的东西
- 关于树状数组的一些讨论
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- linux下memcached的安装、php-memcached拓展安装
- 修改mac环境变量
- SparkSQL相关语句总结
- Hrbust 1259 HaHa's Morning【状压dp---求拓扑排序方案数】
- 从MVC框架看MVC架构的设计
- 关于“树状数组”
- [LeetCode]Surrounded Regions
- iOS学习笔记之GCD详解
- 【修真院“纯洁”系列之二十】除了修真院,还有谁能够在你找工作之前提供真实项目机会?
- 学习Python、Cython、PyCuda
- 常量和基本数据类型
- 服务器:硬RAID与软RAID的区别
- 前端校招面试的一些经验(阿里美团爱奇艺)
- 回溯法求和 算法设计