ACM知识---树状数组

树状数组是一种非常高效的数据结构,不过对于其的使用一般都会有比较明确的数据处理的特点吧,正常的数据处理分为取值和取区间值,如果我们用最为低效的方法:遍历,那么时间复杂度较高,而如果使用前缀和的方式也可以非常快的解决问题,但是前缀和使用的前提是不能修改数据为前提的,如果修改了数据,那么针对数组后面的数据也是需要修改的,那么时间复杂度就较高,而树状数组解决了这个问题,它的原理就是找一个规律用一维数组存储,但是不同于前缀和,前缀和的数组的一个数是与其下标前面所有的数都有关联的于是导致需要修改的数变大,而树状数组是找到一种规律,使得一维数组当中有关联的数据减少,所以修改一个数的时候,只需要修改树下面的子树的数据就可以了,所以修改的次数就是树的深度,最终时间复杂度也就是logn级别的。

存储方式:

我找到的博客当中,存储的方式都是与二进制有关,我介绍一下:

比如你要存一个下标为1的数:其二进制就是0001,1右边的0的个数为0,所以树状数组当中我们下标为1的个数为2的0次方的个元素的和。

如果下标为2,那么其二进制为0010,1右边的0的个数为1,那么下标为2的树状数组当中存储的是2的1次方的个元素的和。

如果下标为3,那么二进制为0011,后面就是一个元素的和。

注意:这里我们知道是几个元素的和之后,最后的元素一定是当前这个数组的坐标。

比如树状数组当中下标为3的,只需要存3下标的一个就可以了。

求元素个数的函数很简单:

 int lowbit(int x){
    return x&(-x);  
}

-x意思是x的二进制码取反再+1,如原来0001,取反后成为1110,再加1,就变成了1111,与后输出,就成了0001,很神奇对吧?这样你就发现他元素个数恰好就是上述的要求。

下面叙述求和操作:

int getsum(int x){
   int sum = 0;
    while(x > 0){
             sum+=A[x];       //A数组表示树状数组
            x-=lowbit(x);  }
    return sum; }

为什么要减去lowbit(x)的呢,因为lowbit表示了以x开始往左边几个下标元素,这里A【x】表示的是从x开始往左数lowbit(x)个值,那么x减去lowbit(x)就表示这几个元素已经加上了,继续循环就可以了。

更新数据操作:

void update(int x,int num){
      while(x <= n){               //n指的是树状数组当中元素个数,这里注意一点树状数组元素个数与原数组个数的元素个数相同
            A[x] +=num;        //num为修改值
            x +=lowbit(x);
 }
这里又为什么加lowbit(x)呢,我们找找规律就可以发现,因为A【x】其中x为最后一个元素,我们可以发现包含x下标的元素在之后出现就只有当x+lowbit(x)的时候包含x下标元素,也是我们需要修改的元素。

这就是树状数组了。

加油,臭咸鱼!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值