当前位置: 首页 > news >正文

(转)树状数组

地址:http://www.cppblog.com/Ylemzy/articles/98322.html

树状数组是对一个数组改变某个元素和求和比较实用的数据结构。两中操作都是O(logn)。 
 在解题过程中,我们有时需要维护一个数组的前缀和S[i]=A[1]+A[2]+...+A[i]。

          但是不难发现,如果我们修改了任意一个A[i],S[i]、S[i+1]...S[n]都会发生变化。

          可以说,每次修改A[i]后,调整前缀和S[]在最坏情况下会需要O(n)的时间。

          当n非常大时,程序会运行得非常缓慢。

          因此,这里我们引入“树状数组”,它的修改与求和都是O(logn)的,效率非常高。

【理论】

          为了对树状数组有个形 象的认识,我们先看下面这张图。

          如图所示,红色矩形表示的数组C[]就是树状数组。

          这里,C[i]表示A[i-2^k+1]到A[i]的和,而k则是i在二进制时末尾0的个数,

          或者说是i用2的幂方和表示时的最小指数。

         ( 当然,利用位运算,我们可以直接计算出2^k=i&(i^(i-1)) )

          同时,我们也不难发现,这个k就是该节点在树中的高度,因而这个树的高度不会超过logn。

          所以,当我们修改A[i]的值时,可以从C[i]往根节点一路上溯,调整这条路上的所有C[]即可,

          这个操作的复杂度在最坏情况下就是树的高度即O(logn)。  

          另外,对于求数列的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。

          不难发现,这些子树的数目是n在二进制时1的个数,或者说是把n展开成2的幂方和时的项数,

          因此,求和操作的复杂度也是O(logn)。

          接着,我们考察这两种操作下标变化的规律:

          首先看修改操作:

          已知下标i,求其父节点的下标。
          我们可以考虑对树从逻辑上转化:


         如图,我们将子树向右对称翻折,虚拟出一些空白结点(图中白色),将原树转化成完全二叉树。

         有图可知,对于节点i,其父节点的下标与翻折出的空白节点下标相同。

         因而父节点下标 p=i+2^k  (2^k是i用2的幂方和展开式中的最小幂,即i为根节点子树的规模)

         即  p = i + i&(i^(i-1)) 。

         接着对于求和操作:

         因为每棵子树覆盖的范围都是2的幂,所以我们要求子树i的前一棵树,只需让i减去2的最小幂即可。

         即  p = i - i&(i^(i-1)) 。

        

         至此,我们已经比较详细的分析了树状数组的复杂度和原理。

         在最后,我们将给出一些树状数组的实现代码,希望读者能够仔细体会其中的细节。

【代码】

  求最小幂2^k:


int Lowbit(int t) 

    return t & ( t ^ ( t - 1 ) ); 

             
  求前n项和:


int Sum(int end) 

    int sum = 0; 
    while(end > 0) 
    { 
        sum += in[end]; 
        end -= Lowbit(end); 
    } 
    return sum; 


 对某个元素进行加法操作: 

void plus(int pos , int num) 

    while(pos <= n) 
    { 
          in[pos] += num; 
          pos += Lowbit(pos); 
    } 
}

转载于:https://www.cnblogs.com/wanglin2011/archive/2013/01/18/2866892.html

相关文章:

  • 有关游戏外挂的一些思考
  • PHP设计模式(2)-原型模式
  • 【自然框架之SSO】实现SSO的一个初步想法
  • 【SAS NOTE】where time
  • Dark Themed Tab View
  • [转]使用自定义命令扩展 Windows PowerShell
  • 事件
  • FutureTask
  • linux并发控制之读写自旋锁
  • javascript访问加runat=server 的Html控件的方法
  • BobMusic
  • WPF个UI元素
  • Homework Exercises 1
  • 图片旋转和翻转
  • atoi简单实现
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • axios 和 cookie 的那些事
  • echarts的各种常用效果展示
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • leetcode-27. Remove Element
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • Sublime Text 2/3 绑定Eclipse快捷键
  • Transformer-XL: Unleashing the Potential of Attention Models
  • vue脚手架vue-cli
  • WordPress 获取当前文章下的所有附件/获取指定ID文章的附件(图片、文件、视频)...
  • 聊聊hikari连接池的leakDetectionThreshold
  • 浅谈web中前端模板引擎的使用
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 突破自己的技术思维
  • 我的面试准备过程--容器(更新中)
  • 用Visual Studio开发以太坊智能合约
  • 责任链模式的两种实现
  • Python 之网络式编程
  • ​MySQL主从复制一致性检测
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • $NOIp2018$劝退记
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (七)理解angular中的module和injector,即依赖注入
  • (三)mysql_MYSQL(三)
  • (译)2019年前端性能优化清单 — 下篇
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • (转载)CentOS查看系统信息|CentOS查看命令
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • .NET 8 中引入新的 IHostedLifecycleService 接口 实现定时任务
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .Net中的集合
  • .net中调用windows performance记录性能信息
  • @Bean注解详解