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








  • 相关阅读:
    hdu 2019 数列有序!
    hdu 2023 求平均成绩
    HDU 5805 NanoApe Loves Sequence (思维题) BestCoder Round #86 1002
    51nod 1264 线段相交
    Gym 100801A Alex Origami Squares (求正方形边长)
    HDU 5512 Pagodas (gcd)
    HDU 5510 Bazinga (字符串匹配)
    UVALive 7269 Snake Carpet (构造)
    UVALive 7270 Osu! Master (阅读理解题)
    UVALive 7267 Mysterious Antiques in Sackler Museum (判断长方形)
  • 原文地址:https://www.cnblogs.com/tigerisland/p/7564556.html
Copyright © 2011-2022 走看看