zoukankan      html  css  js  c++  java
  • 数据结构之树状数组

    原文链接:数据结构之树状数组

    1、概述

    树状数组(binary indexed tree),是一种设计新颖的数组结构,它能够高效地获取数组中连续n个数的和。概括地说,树状数组通常用于解决以下问题:数组{a}中的元素可能不断地被修改,怎样才能快速地获取连续几个数的和?

    2、树状数组基本操作

    传统数组(共n个元素)的元素修改和连续元素求和的复杂度分别为O(1)和O(n)。树状数组通过将线性结构转换成伪树状结构(线性结构只能逐个扫描元素,而树状结构可以实现跳跃式扫描),使得修改和求和复杂度均为O(lgn),大大提高了整体效率。

    给定序列(数列)A,我们设一个数组C满足

    C[i] = A[i–2^k+ 1] + … + A[i]

    其中,k为i在二进制下末尾0的个数,i从1开始算!

    则我们称C为树状数组。

    下面的问题是,给定i,如何求2^k?

    答案很简单:2^k=i&(i^(i-1)) ,也就是i&(-i)

    下面进行解释:

    以i=6为例(注意:a_x表示数字a是x进制表示形式):

    (i)_10 = (0110)_2

    (i-1)_10=(0101)_2

    i xor (i-1) =(0011)_2

    i and (i xor (i-1))  =(0010)_2

    2^k = 2

    C[6] = C[6-2+1]+…+A[6]=A[5]+A[6]

    数组C的具体含义如下图所示:


    当我们修改A[i]的值时,可以从C[i]往根节点一路上溯,调整这条路上的所有C[]即可,这个操作的复杂度在最坏情况下就是树的高度即O(logn)。另外,对于求数列的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。不难发现,这些子树的数目是n在二进制时1的个数,或者说是把n展开成2的幂方和时的项数,因此,求和操作的复杂度也是O(logn)。

    树状数组能快速求任意区间的和:A[i] + A[i+1] + … + A[j],设sum(k) = A[1]+A[2]+…+A[k],则A[i] + A[i+1] + … + A[j] = sum(j)-sum(i-1)。

    下面给出树状数组的C语言实现:

    //求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);
     
      }
     
    }

    3、扩展——二维树状数组

    一维树状数组很容易扩展到二维,二维树状数组如下所示:

    C[x][y] = sum(A[i][j])

    其中,x-lowbit[x]+1 <= i<=x且y-lowbit[y]+1 <= j <=y

    4、应用

    (1)    一维树状数组:

    参见:http://hi.baidu.com/lilu03555/blog/item/4118f04429739580b3b7dc74.html

    (2)    二维树状数组:

    一个由数字构成的大矩阵,能进行两种操作

    1) 对矩阵里的某个数加上一个整数(可正可负)

    2) 查询某个子矩阵里所有数字的和

    要求对每次查询,输出结果

    5、总结

    树状数组最初是在设计压缩算法时发现的(见参考资料1),现在也会经常用语维护子序列和。它与线段树(具体见:数据结构之线段树)比较在思想上类似,比线段树节省空间且编程复杂度低,但使用范围比线段树小(如查询每个区间最小值问题)。

    6、参考资料

    (1)    Binary Indexed Trees:

    http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=binaryIndexedTrees

    (2)    吴豪文章《树状数组》:

    http://www.java3z.com/cwbwebhome/article/article19/zip/treearray.zip

    (3)    郭炜文章《线段树和树状数组》:

    http://poj.org/summerschool/1_interval_tree.pdf








  • 相关阅读:
    Java代理模式
    PHP7.3.0+弃用FILTER_FLAG_SCHEME_REQUIRED的解决办法
    《PHP7底层设计与源码实现》学习笔记1——PHP7的新特性和源码结构
    《MySQL实战45讲》学习笔记2——MySQL的日志系统
    PHP反射学习总结
    依赖注入模式中,为什么用对象而不是用数组传递?
    记MySQL的一次查询经历
    数据结构与算法之PHP递归函数
    PHP的json_encode()函数与JSON对象
    Linux系统如何查看版本信息
  • 原文地址:https://www.cnblogs.com/tigerisland/p/7564557.html
Copyright © 2011-2022 走看看