一、简介
1 定义
树状数组是一种用于维护序列前缀和的数据结构,它支持单点修改和区间查询,时间复杂度均为 O(logn)。树状数组的核心思想是利用二进制的思想将序列分成若干个区间,从而实现快速查询和修改。
2 优点
假设我们有一个序列 a a a,我们需要支持两种操作:
1.单点修改:将 a i a_i ai的值加上 x x x。
2.区间查询:查询 a 1 a_1 a1到 a i a_i ai的和。
如果我们使用普通数组保存序列并实现上述两种操作,则时间复杂度分别为 O ( n ) O(n) O(n)和 O ( 1 ) O(1) O(1)如果使用前缀和,则时间复杂度分别为 O ( 1 ) O(1) O(1)和 O ( n ) O(n) O(n)。假设我们有 n n n个操作,则最坏情况下的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
那么,有没有一种可能使得时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),也就是两个操作的时间复杂度都是 O ( l o g n ) O(logn) O(logn)呢?树状数组便是支持上述两种操作,且时间复杂度为 O ( l o g n ) O(logn) O(logn)的一种数据结构。
二、实现—单点修改+区间查询
假设输入时原数组为 a a a,树状数组为 c c c。
0 预备知识
在位运算中,有一种操作,可以只保留 i i i的二进制表示中最右边的 “ 1 ” “1” “1”: l o w b i t ( x ) = x lowbit(x)=x lowbit(x)=x& ( − x ) (-x) (−x)
int lowbit(int x){
return x&(-x);
}
1 结构

如图,我们可以发现:
c [ 1 ] = a [ 1 ] c[1]=a[1] c[1]=a[1]
c [ 2 ] = a [ 1 ] + a [ 2 ] c[2]=a[1]+a[2] c[2]=a[1]+a[2]
c [ 3 ] = a [ 3 ] c[3]=a[3] c[3]=a[3]
c [ 4 ] = a [ 1 ] + a [ 2 ] + a [ 3 ] + a [ 4 ] c[4]=a[1]+a[2]+a[3]+a[4] c[4]=a[1]+a[2]+a[3]+a[4]
c [ 5 ] = a [ 5 ] c[5]=a[5] c[5]=a[5]
c [ 6 ] = a [ 5 ] + a [ 6 ] c[6]=a[5]+a[6] c[6]=a[5]+a[6]
c [ 7 ] = a [ 7 ] c[7]=a[7] c[7]=a[7]
c [ 8 ] = a [ 1 ] + a [ 2 ] + a [ 3 ] + a [ 4 ] + a [ 5 ] + a [ 6 ] + a [ 7 ] + a [ 8 ] c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8] c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]
如果将它们转变成二进制,可以发现,每一层的 l o w b i t lowbit lowbit都是相同的。
因此,节点 x x x的父节点为 x + l o w b i t ( x ) x+lowbit(x) x+lowbit(x)。
2 操作
2.2 单点更新
更新操作是将输入数组中的一个元素增加一个值。在树状数组中,这需要更新多个元素。具体来说,对于输入数组中位置 i i i的更新,我们需要更新所有包含区间i的树状数组元素。这些元素可以通过在 i i i上加 l o w b i t ( i ) lowbit(i) lowbit(i)得到:
c [ i ] = a [ i − 2 k + 1 ] + a [ i − 2 k + 2 ] + … … a [ i ] c[i]=a[i-2^k+1]+a[i-2^k+2]+……a[i] c[i]=a[i−2k+1]+a[i−2k

本文介绍了树状数组,它是用于维护序列前缀和的数据结构,支持单点修改和区间查询,时间复杂度为O(logn)。详细阐述了单点修改+区间查询、区间修改+单点查询、区间修改+区间查询三种情况的实现结构、操作方法,并给出实践例题及AC代码,最后提到树状数组扩展性小。

868

被折叠的 条评论
为什么被折叠?



