【数据结构之树状数组】

转载 2015年07月11日 00:54:07
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
 
intlowbit(intt)
 
{
 
    returnt & ( t ^ ( t - 1 ) );
 
}
 
//求前n项和
 
intsum(intend)
 
{
 
   intsum = 0;
 
   while(end > 0)
 
  {
 
     sum += in[end];
 
     end -= lowbit(end);
 
  }
 
  returnsum;
 
}
 
//增加某个元素的大小
 
voidplus(intpos, intnum)
 
{
 
   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

----------------------------------------------------------------------------------------------
更多关于数据结构和算法的介绍,请查看:数据结构与算法汇总
----------------------------------------------------------------------------------------------

原创文章,转载请注明: 转载自董的博客

本文链接地址: http://dongxicheng.org/structure/binary_indexed_tree/

作者:Dong,作者介绍:http://dongxicheng.org/about/

本博客的文章集合:


[数据结构].树状数组

树状数组定义、操作、实现与例题。
  • Stan_Chou
  • Stan_Chou
  • 2016年08月06日 19:11
  • 425

树状数组求区间最大值

讲这个的博文已经不少了,但感觉不够详细不够通俗易懂,所以我尝试着更详细更通俗易懂的说一下我的理解。   这个算法只支持单点修改和区间查询最值。每一次维护和查询的时间复杂度都是O((logn)^2),但...
  • u010598215
  • u010598215
  • 2015年09月04日 10:42
  • 4848

树状数组 讲解和题目集

树状数组     树状数组作为一种实现简单、应用较广的高级数据结构,在OI界的地位越来越重要,下面我来简单介绍一下树状数组和它的简单应用。 一、树状数组简介 树状数组:顾名思义,是一种数...
  • moep0
  • moep0
  • 2016年10月09日 21:28
  • 792

C++树状数组详解

引入 如果给你n个数,然后进行q次询问,每次询问一个区间[x,y]的和,你会怎么做? 第一种方法:最简单的方法,用数组存起来,每次枚举x-y,ans加起来就可以,时间复杂度O(qn),十分慢。 ...
  • C20190102
  • C20190102
  • 2017年04月28日 13:32
  • 1346

树状数组题集[不断更新]

一. 基本原理 树状数组中用的d【】,每个点都有一定的管辖范围; 如c[1]=a[1]; d[2]=a[1]+a[2]; d[3]=a[3]; d[4]=a[1]...
  • Matrix_Reloaded
  • Matrix_Reloaded
  • 2014年06月18日 11:40
  • 3001

hdu 5147 Sequence II(树状数组,前缀和,后缀和)

Sequence II Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) To...
  • acm_cxq
  • acm_cxq
  • 2016年07月14日 23:01
  • 409

树状数组---原理代码实现

刚刚学了树状数组,有必要总结一下;参考了大牛的博客
  • u011644423
  • u011644423
  • 2014年08月01日 10:21
  • 1605

对树状数组的一点理解

树状数组 一、概述 树状数组是一种 用数组进行存储的 自下而上进行操作的  多叉树。 最基本的应用就是维护一个支持两种操作的数列:1.让A[i]加上某数X     2.求一个区间A[L] + A[L+...
  • u012891242
  • u012891242
  • 2015年04月13日 17:01
  • 1243

关于树状数组一些有意思的东西

嘛~最近刚刚学会树状数组,写个blog记录一下心得。树状数组呢,核心是一个叫lowbit的东西,lowbit(x)=x&-x=x的最后一位1的大小。 一、一个经典问题 一个初始值为0的k位计数器...
  • TA201314
  • TA201314
  • 2016年04月28日 09:23
  • 1428

树状数组总结

对于普通数组,其修改的时间复杂度位O(1),而求数组中某一段的数值和的时间复杂度为O(n),因此对于n的值过大的情况,普通数组的时间复杂度我们是接受不了的。 在此,我们引入了树状数组的数据结构,它能在...
  • HowardEmily
  • HowardEmily
  • 2017年02月10日 22:01
  • 642
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【数据结构之树状数组】
举报原因:
原因补充:

(最多只允许输入30个字)