优雅的数据结构---树状数组.pdfVIP

  1. 1、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。。
  2. 2、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载
  3. 3、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
  4. 4、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
  5. 5、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们
  6. 6、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
  7. 7、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
优雅的数据结构---树状数组

优雅的数据结构 树状数组 2016 SJTU ACM Class lwher 什么是树状数组? • 其原理是什么? • 它能做什么? • 时空复杂度? • 好不好写? • •太麻烦了! •我们先来直接讲个例子,感受一下它的 神奇吧! 问题 • 给一个长度为n的数组,每次可以进行两种操作: • (1)单点增加一个数值 • (2)查询给定区间的和 • n = 10^5 操作数=10^5 • 怎么做? • 每次直接修改,然后查询的时候for一下? • TLE! • 利用前缀和每次快速查询,然后修改的时候重构? • TLE! • 啥?你说你会线段树?还会分块? • ...Naive! • 让我们来看看树状数组的代码是怎么写的吧。 • void Add(int x, int p){ //在x处增加p的值 • for(int i = x; i = n; i+= (i -i)) T[i] += p; • } • int Query(int x){//查询1~x的区间和 • int res = 0; • for(int i = x; i; i -= (i -i)) res += T[i]; • return res; • } • 主函数 • 一开始我们认为数组全是0 , 然后如果数组有初始值,就 直接看成单点增加就OK 了 • 询问?假设问[l, r]和,那么答案就是Q(r) - Q(l - 1) • 没了So 优雅! • 很神奇吧? • 那这么优雅的数据结构,它的原理是什么呢? • 其实这个相对于它的代码来说,还是复杂了一点,我们来 慢慢看 • 首先解决一个问题: • i -i 是啥?(后面也写作lowbit) • -i 是在 i 的基础上把二进制的符号位变化,并且其他位取反 然后+1,是位运算,a b 的算法就是把a,b的二进制形 式右对齐,然后如果全是1,这位还是1,否则为0 • 举个例子 10100 的 lowbit 就是 100 • i 和 -i 实际上除了i的最后一个1那位,其他位置都不一样, 所以我们得到了i的二进制下最右边的那个1 • 接下来,我们来看看树状数组直观上看是长啥样的。 • 二进制十分的美妙!利用lowbit,我们发现从1~n刚好串成 了一个树形结构。 • 树状数组是如何实现快速求1~n位的和的呢? • 其实通俗的来讲,树状数组的每一位C[i],除了存储num[i] 之外,还存了其与一些其他的数字加起来的和,我们可以 认为每个C[i]有一个自己负责的区域( 自己为根的子树), 那么我们在查询的时候,利用lowbit往下走,1~n这个区间 又被刚好分成了log个毫无交集的“区域” ,并且他们的并集 就是1~n,所以相加就好了(每次-=lowbit ,那么就是每次 消除二进制下最后一个1,最多log个1,所以是log次) • 修改操作: • 其实原理上差不多,每次+=lowbit,刚好实现了在树中一 层一层往上走的情况。因为每个C[i]统计的是以i为根的子 树的和,所以如果修改i的数字,那么i的祖先也都要一一修 改。 • (如果觉得上面的解释不好懂,那么我们换一种说法,树状 数组可以理解成每个C[i]有一个自己负责的“区域” ,然后关 系又是一级一级的,每个C[i]最多只会有一个“Boss” ,每个 C[i]负责自己的下属与下属的下属的和,那么修改的时候就 要“层层汇报” ,表示一下自己被改了,上级全部修改) • 从理论上来解释一发 • 存储原理: • 对于每个C[i]负责的区域,其实是这个i二进制表示下,去 掉去lowbit再+1 一直到 i 这个区间。 • 比如: 1101000负责的是1100001 ~ 1101000这个部分 • 所以我们修改的时候,每次加lowbit,相当于把最后的1一 直左移,遇到前面也是1的就也消去它再往前走。 • 查询也是一样的,每次查询 [x-lowbit(x)+1,x]然后去掉 • 比如1010 - 先查了1001 ~ 1010 ,然后x变成1000, • 接着加上 0001 ~ 1000 这刚好是前缀和。 • 时间复杂

文档评论(0)

laolao123 + 关注
实名认证
文档贡献者

该用户很懒,什么也没介绍

1亿VIP精品文档

相关文档