树状数组(binary indexed tree)

树状数组是一种用于查询区间和的数据结构。

比如对于数组a[1,2,3,4,5,6,7,8,9,10],我们要知道它的前4项和,我们就需要把这前四个一个个加起来才能得到结果,也就是O(K)(前k项和)的复杂度。如果查询次数少,k也比较小还可以接受,但是在大规模的查询和数据量时,这个复杂度就太高了。树状数组就是为了解决这个问题而创造出来的,它能在log(k)的复杂度内查询区间和。

树状数组其实就是把一颗二叉树压缩在了一个数组里,看下图(一如既往的丑):

 

对于数组[1,2,3,4],我们把它转化为一个求和树,每个节点的值就是它的两个子节点的值,此时我们要求前k项就比较好求了,比如求前4项,我们可以直接给出节点10。但是我们如何用一个树组来表示它呢?如果要每个节点都有一个位置对应,起码得有2n个节点,但是我们只用n个。考虑b,d两个节点,其实b里面已经包含了d,所以我们可以把他们合并。同样,a里面其实包含了e,g他们也可以合并到位置4。

  我们来看看一般的规律(下面的位置索引都是二进制值):

索引包含值
001(1)001
010(2)001 010
011(3)011
100(4)001 010 011 100
 不难看出一个位置包含的区间,其实就是它的二进制表示去掉从右到左第一个1得到的值 ——>该位置。

要得到从左到右第一个1也很简单 x&(-x)即可。

下面是具体代码:

#include<iostream>
using namespace std;
//Binary Indexed Tree(BIT)
//得到x只剩下从右到左第一个一时的值。
 int getk(int x){
  return x&(-x);
}
//由正常的数组建立树状数组
 void build(int s[],int size){
  for(int i=1;i<=size;i++){
    int next = getk(i)+i;
    if(next<=size)s[next-1] += s[i-1];
  }
}
//更新一个位置的值
void update(int s[],int size,int k,int dt){
  k++;
  while(k<=size){s[k-1]+=dt;k+=getk(k);}
}
//求前k项和
int ksum(int s[],int size,int k){
  int result = 0;
  while(k>1){cout << k << endl;result += s[k-1];k -= getk(k);}
  return result;
}
int main(){
  int a[10] = {1,2,3,4,5,6,7,8,9,10};
  build(a,10);
  cout << ksum(a,10,4) << " " << ksum(a,10,7) << endl;
  update(a,10,4,-1);
  for(int i=0;i<10;i++){
    cout << a[i] << " ";
  }
  cout << endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值