树状数组的基本操作流程与线段树很像,故学习起来时是两者的操作对比起来学的。线段树学习之单点更新与区间更新
概念
树状数组(Binary Indexed Tree)是一种与线段树类似的数据结构,又叫二进制索引树(spoil alert :跟二进制有关),主要用于查询区间和问题,每次只能修改一个元素的值(加入多个辅助数组后可实现区间修改和区间查询)。
树状数组的节点比线段树少,但也能表示一个数组,
C
C
C数组为每一列的顶端节点,
C
[
i
]
C[i]
C[i] 是子树的叶子节点的权值之和。
若一个数组 A A A有8个数,那么它的树状数组 C C C如图所示
由图我们易看出,
C[(0001)1] = A[1]
C[(0010)2] = A[1] + A[2]
C[(0011)3] = A[3]
C[(0100)4] = A[1] + A[2] + A[3] + A[4]
................................
C[(1000)8] = A[1] + A[2] + A[3] +...+ A[7] + A[8]
结合二进制会发现,C[n]=A[n-2^k+1]+...+A[n]
由以上性质,我们引申出lowbit函数。
lowbit函数
在算 2 k 2^k 2k时,我们可以使用快速幂的方法,但利用机器补码算起来会更快。lowbit函数用于将一个二进制数所有高位1都去掉,只留下最低位的1,比如lowbit(3)=lowbit(0011(二进制))=0001=1
int lowbit(int x) {
return x & -x;
}
基本用法
单点修改
若想对 x x x实现单点修改,则需要加上自己的lowbit,直到 x ≤ n x \leq n x≤n,从而维护树状数组。
//更新,单点修改
void update(int x,int k) {
while(x<=n){
tree[x] += k;
x += lowbit(x);
}
}
单点查询
//单点查询
int search(int x) {
int ans = 0;
while(x!=0){
ans += tree[x];
x -= lowbit(x);
}
}
区间修改
void add(int x,int k) {
while(x<=n){
tree[x] += k;
x += lowbit(x);
}
}
区间查询
void add(int x,int k) {
while(x<=n){
tree[x] += k;
x += lowbit(x);
}
}
初始化
//初始化树状数组
void InitTree(int num) {
for(int i=1;i<=num;i++){
int a;
scanf("%d",&a);
add(i,a);
}
}
例题
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
int tree[2000010];
int lowbit(int x) {
return x & -x;
}
//区间查询
int sum(int x) {
int ans =0;
while(x!=0){
ans += tree[x];
x -= lowbit(x);
}
return ans;
}
//区间修改
void add(int x,int k) {
while(x<=n){
tree[x] += k;
x += lowbit(x);
}
}
//初始化树状数组
void InitTree(int num) {
for(int i=1;i<=num;i++){
int a;
scanf("%d",&a);
add(i,a);
}
}
int main(){
int Case,t;
char str[11];
scanf("%d",&t);
for(Case = 1;Case<=t;Case++){
scanf("%d",&n);
memset(tree,0,sizeof(tree));
InitTree(n);
printf("Case %d:\n",Case);
while(~scanf("%s",str)){
if(str[0] == 'E'){
break;
}
int x,y;
scanf("%d%d",&x,&y);
if(str[0] == 'Q'){
cout << sum(y)-sum(x-1) <<endl;
}
if(str[0] == 'S'){
add(x,-y);
}
if(str[0] == 'A'){
add(x,y);
}
}
}
return 0;
}
与线段树比较
树状数组可以高效的进行区间统计,思想类似于线段树,但是比线段树节省空间(开两倍的原数组就ok,线段树4倍),代码复杂度比线段树低,实际查询时间也比线段树快(树状数组最坏 l o g ( n ) log(n) log(n),线段树 n l o g ( n ) nlog(n) nlog(n).但是!树状数组的适用范围比线段树小。
这周只写了一题,下周会努力把其他的也补起来的orz