掌握树状数组~彻底入门

作者:霜雪千年
出处:http://www.cnblogs.com/acgoto/
先贴一下树状数组的模板代码:

int lowbit(int i)
{return i & -i;//或者是return i-(i&(i-1));表示求数组下标二进制的非0最低位所表示的值
}
void update(int i,int val)//单点更新
{while(i<=n){C[i]+=val;i+=lowbit(i);//由叶子节点向上更新树状数组C,从左往右更新}
}
int sum(int i)//求区间[1,i]内所有元素的和
{int ret=0;while(i>0){ret+=C[i];//从右往左累加求和i-=lowbit(i);}return ret;
}

模板中最常见的三个函数:①取数组下标二进制非0最低位所表示的值;②单点更新;③区间查询。树状数组,顾名思义是树状的数组,我们首先引入二叉树,叶子节点代表A[1]~A[8]。

现在变形一下:

现在定义每一列的顶端节点C数组(其实C数组就是树状数组),如图:

C[i]代表子树的叶子节点的权值之和,如图可以知道:

C[1]=A[1];
C[2]=A[1]+A[2];
C[3]=A[3];
C[4]=A[1]+A[2]+A[3]+A[4];
C[5]=A[5];
C[6]=A[5]+A[6];
C[7]=A[7];
C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];

将C数组的下标i转化成二进制:

1=(001) C[1]=A[1];
2=(010) C[2]=A[1]+A[2];
3=(011) C[3]=A[3];
4=(100) C[4]=A[1]+A[2]+A[3]+A[4];
5=(101) C[5]=A[5];
6=(110) C[6]=A[5]+A[6];
7=(111) C[7]=A[7];
8=(1000) C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];

对照式子可以发现:C[i]=A[i-2^ k+1]+A[i-2^k+2]+…+A[i];(k为i的二进制中从最低位到最高位连续零的个数)

例如:当i=8时,k=3,可以自行代入验证。现在引入lowbit(x):其实就是取出x的二进制的最低位1,换言之,lowbit(x)= 2^k,k的含义与上面相同。

int lowbit(int i)
{return i&(-i);
}
/*
-i 代表i的负数 计算机中负数使用对应的正数的补码来表示
例如 : i=6(0110) 此时 k=1
-i=-6=(1001+1)=(1010)i&(-i)=(0010)=2=2^1
C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i];
C[i]=A[i-lowbit(i)+1]+A[i-lowbit(i)+2]+......A[i];
*/

接下来是区间查询(求和):利用C[i]数组,求A数组中前i项和:举两个栗子:

①i=7,前7项和:sum[7]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7];

C[4]=A[1]+A[2]+A[3]+A[4]C[6]=A[5]+A[6]C[7]=A[7]
可以得到:sum[7]=C[4]+C[6]+C[7]

数组下标写成二进制:sum[(111)]=C[(100)]+C[(110)]+C[(111)]

②i=5,前5项和:sum[5]=A[1]+A[2]+A[3]+A[4]+A[5]

C[4]=A[1]+A[2]+A[3]+A[4]C[5]=A[5];可以得到:sum[5]=C[4]+C[5]

数组下标写成二进制:sum[(101)]=C[(100)]+C[(101)]

细细观察二进制,树状数组追其根本就是二进制的应用,结合代码演示一下代码过程:

int sum(int i)//求区间[1,i]所有元素的和
{int ret=0;while(i>0){ret+=C[i];//从右往左区间求和i-=lowbit(i);}return ret;
}

对于i=7进行演示:

                       7(111)  ans+=C[7]

lowbit(7)=001 7-lowbit(7)=6(110) ans+=C[6]
lowbit(6)=010 6-lowbit(6)=4(100) ans+=C[4]
lowbit(4)=100 4-lowbit(4)=0(000) break;

对于i=5进行演示:

                       5(101)  ans+=C[5]

lowbit(5)=001 5-lowbit(5)=4(100) ans+=C[4]
lowbit(4)=100 4-lowbit(4)=0(000) break;

最后是单点更新:当我们修改A数组中某个值时,应当如何更新C数组呢?回想一下,区间查询的过程,再看一下上文中列出的过程。这里声明一下:单点更新实际上是不修改A数组的,而是修改树状数组C,向上更新区间长度为lowbit(i)所代表的节点的值。

void update(int i,int val)//更新单节点的值
{while(i<=n){C[i]+=val;i+=lowbit(i);//由叶子节点向上更新a数组}
}
//可以发现 更新过程是查询过程的逆过程
//由叶子结点向上更新C[]数组

如图:当在A[1]加上值val,即更新A[1]时,需要向上更新C[1],C[2],C[4],C[8],这个时候只需将这4个节点每个节点的值加上val即可。这里为了方便大家理解,人为添加了个A数组表示每个叶子节点的值,事实上A数组并不用修改,实际运用中也可不设置A数组,单点更新只需修改树状数组C即可。下标写成二进制:C[(001)],C[(010)],C[(100)],C[(1000)];
lowbit(1)=001 1+lowbit(1)=2(010) C[2]+=val;
lowbit(2)=010 2+lowbit(2)=4(100) C[4]+=val;
lowbit(4)=100 4+lowbit(4)=8(1000) C[8]+=val;

最后说一下树状数组的优缺点:①特点:代码短小,实现简单;容易扩展到高纬度的数据;

②缺点:只能用于求和,不能求最大/小值;不能动态插入;数据多时,空间压力大。

掌握树状数组~彻底入门相关推荐

  1. Matrix(二维树状数组)入门第一题

    题目链接:http://poj.org/problem?id=2155 Matrix Time Limit: 3000MS   Memory Limit: 65536K Total Submissio ...

  2. 关于树状数组的个人理解

    本篇只对树状数组的理论部分进行讲解,其应用在之后的博客中再进行专题讲解 以下内容部分转载于: 彻底理解树状数组 树状数组彻底入门 树状数组(Binary Indexed Tree)--二进制索引树 树 ...

  3. **PAT_甲级_1057 Stack (30分) (C++)【字符串处理/栈的模拟/树状数组】

    目录 1,题目描述 题目大意 2,思路 数据结构 函数讲解 1,void update(int x, int v): 2,int getsum(int x): 3,void PeekMedian(): ...

  4. 线。段。树--树状数组-主席树

    简单了解一下线段树 以前写过的内容,搬运过来 线段树的应用场景:满足区间加法性质且多次查询,什么是区间加法性质,比如最大值,求和,树状数组.线段树.主席树依次. 线段树框架:建树--查询--更新... ...

  5. 树状数组(未填完的坑)

    Flag:八月底开学前写完!!! 介绍 树状数组(Binary Indexed Tree)其实是一种简单的数据结构,因为简单易懂经常代替线段树来求数列的前缀和.区间和等 原理 很久很久以前,有一个聪明 ...

  6. 树状数组入门——以洛谷3374为例

    树状数组入门 含义:顾名思义,用树状表示的数组 功能:是一个查询和修改复杂度都为log(n)的数据结构.主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值:经过简单修改可以在log( ...

  7. ACM入门之【树状数组】

    树状数组和线段树具有相似的功能,但他俩毕竟还有一些区别:树状数组能有的操作,线段树一定有:线段树有的操作, 树状数组不一定有.但是树状数组的代码要比线段树短,思维更清晰,速度也更快,在解决一些单点修改 ...

  8. ACM入门之【树状数组习题】

    目录 130. 树状数组 1 :单点修改,区间查询 131. 树状数组 2 :区间修改,单点查询 132. 树状数组 3 :区间修改,区间查询 133. 二维树状数组 1:单点修改,区间查询 134. ...

  9. [UVALive - 4329] Ping pong 树状数组入门

    题目链接:Ping pong 题意 给你n个数,你从中取3个数,要求中间的数字大小在两边数字之间.问你总共有多少种取法. 题解 这个题首先需要分析转化. 假设第i个人作为中间数 a1-ai−1有ci个 ...

最新文章

  1. 『干货』分享你最喜欢的技巧和提示(Xcode,objective-c,swift,c...等等)
  2. 图像拼接--Coarse-to-fine Seam Estimation for Image Stitching
  3. jenkins安装与配置---window,mis包直接安装
  4. 成功输出消息后的采购订单不能取消审核
  5. JS / 闭包的理解
  6. XmlPullParserException
  7. 汇编语言(王爽第三版) 实验5编写、调试具体多个段的程序
  8. 如何用SQL语句查询Excel数据
  9. JQuery实用技巧--学会你也是大神(1)——插件的制作技巧
  10. jogbuild-common.xml:17: Cannot find /home/tsit/tio-software/jogamp/gluegen/make/gluegen-cpptasks.xml
  11. html =拼接dom,在js代码拼接dom对象到页面上去的模板总结(必看)
  12. 多周期MIPS CPU硬布线控制器设计
  13. android屏幕适配无效_Android 屏幕适配终结者
  14. 分析算法泛化性能的有效工具:偏差——方差分解
  15. 电影院票务管理系统数据库设计
  16. 《老路:用得上的商学课》读书笔记-004 边际成本
  17. win10微信打电话对方听不到你的声音,你能听到对方声音
  18. Java中占位符的实战运用
  19. Thymeleaf模板(全程案例详解)
  20. GitLab的使用之Git-biz push失败问题整理

热门文章

  1. Eslint 规则说明
  2. hive-diea-ETL数据截取split,嵌套SQL查询,ETL-SQL表查询中间件解析
  3. [安洵杯 2019]iamthinking
  4. 高德地图 JS API (地图/地图属性)
  5. ASIC加速技术在航空航天领域的应用:提高飞行器速度和稳定性
  6. iOS人机界面指南(界面设计基础部分)ISUX原创翻译
  7. 使用STM32的串口进行大量数据传输
  8. [前端】页面显示不出来文字,但是检查元素没问题
  9. php yii框架addselect,PHP Yii框架之数据库查询操作总结
  10. 永磁同步电机矢量控制(FOC)之:电压前馈补偿型电流控制方案