时间复杂度
树状数组是一个查询和修改的时间复杂度都为 l o g ( n ) log(n) log(n)的数据结构。一般来说树状数组能解的题目,线段树都能解,但是线段树能解的问题,树状数组不一定能解。但是树状数组的有点在于方便实现,代码量少。
树状数组的结构
图中的A数组是原来的数组,C数组就是树状数组,如果先将A数组的每个元素都先放在C数组中,则:
C数组中每一位所存的信息都由前面的某些位的信息构成,以求A的前缀和为例
C
1
C_1
C1中存放的和就是
C
1
C_1
C1
C
2
C_2
C2中存放的和是
C
1
+
C
2
C_1+C_2
C1+C2
C
3
C_3
C3中存放的和就是
C
3
C_3
C3
C
4
C_4
C4中存放的和就是
C
2
+
C
3
+
C
4
C_2+C_3+C_4
C2+C3+C4
…
如何去寻找树状数组中每一位所对应的其他位
假设当前位是i,现在要将i位置的和加到i后面的某些位上。
树状数组每个位置的下标其实是以二进制来对应的,例如:
1d=1b
2d=10b=1b+1b
3d=11b
4d=100b=11b+1b=10b+10b
即将i写成2进制,并从右往左找,找到第一个1,并用i加上这个1所对应的的权值,就是i后面的第一个要找的位。
那么如何高效的寻找上述的1成为这个数据结构的关键。
对任意的一个数i(二进制是xxxx1...
),它的相反数-i(二进制是XXXX1...
)
上面表达式二进制中的xxxx
取反是XXXX
,...
表示不定数量的0
那么求从右往左的第一个1所对应的的权值那就是
i
&
(
−
i
)
i\&(-i)
i&(−i),一般用lowbit(i)
表示这个值
如何构建和更新树状数组
对树状数组而言,构建和更新其实是一样的操作:
- 先将A数组中的每一个数都放在C数组的 1 1 1~ N N N中
- 然后对C数组中的每一个元素将其下标i不断地加上
lowbit(i)
,直到大于 N N N为止,这样一个树状数组就构建完成
如何查询前缀和
查询A数组中下标为i的前缀和
- 先定义一个sum存放和
- sum不断地累加 C i C_i Ci,且每次累加后令 i − = l o w b i t ( i ) i-=lowbit(i) i−=lowbit(i),直到i<1为止
练手题:如何用树状数组求数组中的中位数