树状数组
来源:互联网 发布:中国外来物种数据库 编辑:程序博客网 时间:2024/05/29 08:18
树状数组
树状数组: 也叫做二分索引数, 主要采用二分的思想,这种方式的提出,主要是因为 任何整数都可以写成 2的幂的 和的形式 ,因此 形成了一串序列可以写成一系列子串的和
1、例如: 元数据 a[] , 树状数组c[] c[n] 表示的段是 c[n] = a[n-2^k +1 ] + a[n-2^k] + ......+ a[n]
注意 k 表示的是二进制 下末尾 0 的个数.....
要思考的问题是如何 根据 n 求出2^ k 的值
如图所示:
c[1] = a[1] ;
c[2] = a[1] + a[2] ;
c[3] = a[3] ;
c[4] = c[2] + c[3] + a[4] = a[1] +a[2] +a[3] + a[4] ;
c[5] = a[5];
c[6] = a[5] + a[6] ;
。。。。。。
1) 上面继续想问题 k 表示的是 n 在二进制下0 的个数 例如 6 (110) 结果是 1 输出 2^k = 2 用什么方法来得到呢?
2^k = n and (n xor (n-1))
这个公式不难推导 先验证一下正确性 110 and(110 xor 101) = 110 and 011 = 010 = 2D
结果是对的.. 至于怎么来的其实我也不知道 也可以写成 2 ^k = x and(-x) -x 表示的是x 的补码 即 取反加1
代码:
int lowbit(int x){//函数功能主要是根据n 求2^k 方便动态维护和查询更新 return x&(-x);}
2) 要想的问题就是 更新维护操作了(下面讨论一维的)
这里解释为什么 要有更新操作 当原数组在pos位置 加入一个数w , 又因为数组数组c[n ] 表示的是一段数组
要更新的东西不仅仅是一个了,是很多个 而且是向上更新 从包含x 的开始向上更新
void update1(int pos, int w){ //这里讨论的是一维的 /*也可以写成 while(pos<=n) { c[pos] += w; pos+= lowbit(pos); } */ int i; for(i = pos;i <= n ; i+=lowbit(i)) { c[pos] += w; }}
下面是二维数组的更新和维护函数(其实如果再加上循环的话就可以建立一个树状数组了
void update2(int x, int y,int w){//这里是二维的树状数组的更新方便查询和维护 int i , j; for(i = x ; i <= n ; i +=lobit(i)) { for(j = y ; j <= n ;j+=lowbit(j)) { c[i][j] += w; } }}
3)下面要讨论的就是求和操作了 其实树状数组的产生也是为了降低复杂度而生成的 求和操作也可以说成是查询
这里要注意的是 n = n- lowbit(n) 的意思 这个意思是 将N 转换成二进制后最后的那个1 去掉
例如: 上面 6 110 -lowbit(6) 即 110 - 010 得到100 6-2 = 4
对于N 其实最多有logN个1 这个很容易理解 因此查询效率可以降低到log(N)
先给出二维树状数组求和操作的代码:
例如要求解区间[ a, b] 之间的和 要解决的问题是 求解sum(a, b) = sum (1, b) - sum( 1, a-1) 因此可以方便求解了
int sum(int x, int y){ /*也可以写成 int sum = 0; int tmp; while( x > 0 ) { tmp = y; while(tmp >0) { sum += c[i][tmp] ; tmp -= lowbit(tmp); } i -= lowbit(i); } return sum; */ int sum = 0; int i , j; for(i= x ; i > 0 ; i -=lowbit(i)) { for(j = y ; j > 0 ; j -= lowbit(j)) { sum += c[i][j]; } } return sum;}其次给出一维树状数组的求和代码:
int sum1(int en){//这个主要是一维树状数组的求和问题了 /*也可以写成 int sum = 0; int tmp = en; while(tmp > 0) { sum += c[tmp] ; tmp -= lowbit(tmp); } return sum ; */ int j ; int sum = 0 for(j = en ; j > 0 ;j-= lowbit(j)) { sum += c[j]; } return sum;}
下面要求解的问题就是复杂度的问题了
树状数组能有效的改善求和问题的复杂度,但是也有他一定的局限性。。
先说明一下局限性:1)要对一个区间内进行更新 这个时候树状数组就不能有效的进行操作了
2) 对于不能有效的利用 sum(a, b) = sum(1, b) - sum(1, a-1) 进行操作的也是不可以的
下面就开始说明复杂度问题了:
更行操作 每次都市lowbit(N) 一下因此复杂度可以说是O(log(n))
求和操作或者说是查询操作复杂度也是 O(log(n))
以上每次都市减掉或者加上最低位1 复杂度可以降低到 logn
以上差不多就是树状数组的操作了,以后会补上各种题目进行练习
下面给出木模板 :
#include<iostream>using namespace std;int lowbit(int x){//函数功能主要是根据n 求2^k 方便动态维护和查询更新 return x&(-x);}void update1(int pos, int w){ //这里讨论的是一维的 /*也可以写成 while(pos>0) { c[pos] += num; pos-= lowbit(pos); } */ int i; for(i = pos;i > 0 ; i-=lowbit(i)) { c[pos] += w; }}void update2(int x, int y,int w){//这里是二维的树状数组的更新方便查询和维护 int i , j; for(i = x ; i <= n ; i +=lobit(i)) { for(j = y ; j <= n ;j+=lowbit(j)) { c[i][j] += w; } }}int sum1(int en){//这个主要是一维树状数组的求和问题了 /*也可以写成 int sum = 0; int tmp = en; while(tmp > 0) { sum += c[tmp] ; tmp -= lowbit(tmp); } return sum ; */ int j ; int sum = 0 for(j = en ; j > 0 ;j-= lowbit(j)) { sum += c[j]; } return sum;}//下面的代码查询a[1][1] ~ a[i][j] 之间的和int sum2(int x, int y){ /*也可以写成 int sum = 0; int tmp; while( x > 0 ) { tmp = y; while(tmp >0) { sum += c[i][tmp] ; tmp -= lowbit(tmp); } i -= lowbit(i); } return sum; */ int sum = 0; int i , j; for(i= x ; i > 0 ; i -=lowbit(i)) { for(j = y ; j > 0 ; j -= lowbit(j)) { sum += c[i][j]; } } return sum;}int main(){ return 0;}
0 0
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- bundle的整个类加载顺序
- Android基础---重建Activity
- 别堕落,你没资格(转给自己)
- 消息队列
- 利用Eclipse中的Maven构建Web项目(三)
- 树状数组
- 蜗牛—JavaScript学习之简易计算器
- 类加载器
- Linux内核启动及文件系统加载过程
- android layout与view机制
- Java线程池介绍(ExecutorService)
- Android基础---把用户带入另外一个应用程序(一)
- Windows N6.2以上执行计划任务
- 一个小想法