树状数组学习总结(动态求取区间和、单点元素值)

树状数组

树状数组是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。

求和操作

   (1)树状数组中的每个元素是原数组中一个或者多个连续元素的和。

 (2)在进行连续求和操作a[1]+...+a[n]时,只需要将树状数组中某几个元素的和即可。时间复杂度为O(lgn)

 (3)在进行修改某个元素a[i]时,只需要修改树状数组中某几个元素的和即可。时间复杂度为O(lgn)


 解释如下:

  1) a[]: 保存原始数据的数组。(操作(1)求其中连续多个数的和,操作(2)任意修改其中一个元素)

    e[]: 树状数组,其中的任意一个元素e[i]可能是一个或者多个a数组中元素的和。如e[2]=a[1]+a[2]; e[3]=a[3]; e[4]=a[1]+a[2]+a[3]+a[4]。 

  2) e[i]是几个a数组中的元素的和?

    如果数字 i 的二进制表示中末尾有k个连续的0,则e[i]是a数组中2^k个元素的和,则e[i]=a[i-2^k+1]+a[i-2^k+2]+...+a[i-1]+a[i]。

    如:4=100  e[4]=a[1]+a[2]+a[3]+a[4]; lowbit(4)=4,即四个元素和;

      6=110 e[6]=a[5]+a[6]; lowbit(6)=2,即二个元素和;

      7=111  e[7]=a[7]; loebit(7)=1,即一个元素和;

  3) 后继:可以理解为节点的父亲节点。离它最近的,且编号末位连续0比它多的就是的父亲,如e[2]是e[1]的后继;e[4]是e[2]的后继。

      如e[4] = e[2]+e[3]+a[4] = a[1]+a[2]+a[3]+a[4] ,e[2]、e[3]的后继就是e[4]。

      后继主要是用来计算e数组,将当前已经计算出的e[i]添加到他们后继中。

    前驱:节点前驱的编号即为比自己小的,最近的,最末连续0比自己多的节点。如e[7]的前驱是e[6],e[6]的前驱是e[4]。

        前驱主要是在计算连续和时,避免重复添加元素。

      如:Sum(7)=a[1]+...+a[7]=e[7]+e[6]+e[4]。(e[7]的前驱是e[6], e[6]的前驱是e[4])

    计算前驱与后继:

      lowbit(i) = ( (i-1) ^ i) & i ;

      节点e[i]的前驱为 e[ i - lowbit(i) ];

      节点e[i]的后继为 e[ i + lowbit(i) ];

计算机中如何求lowbit(i)的值:

int lowbit(int i )
{
     return i&(-i);    
}
求区间元素和:

int Sum(int n)//有n个元素
{
    int sum=0;
    while(n>0)
    {
         sum+=c[n];
         n=n-lowbit(n);//求前驱元素,一直到全部加上
    }    
    return sum;
}//求某一段区间(a,b)的元素和可以为Sum(a)-Sum(b-1);
当区间某个元素值改变时:(某个结点i加上x)

void change(int i,int x)
{
     while(i<=n)
     {
          c[i]=c[i]+x;
          i=i+lowbit(i);//后继都要加上x,后继的值依赖于前驱。
     }
}

    来个例题巩固巩固: 士兵杀敌(二)

以上是求某个区间的和,但是如果每次修改不是某个区间的某个元素值改变,而是一个区间的每个元素都统一加或减某一个数,然后查询某个下标元素的值,我们该怎么做?
    上面的int Sum ( int  n )函数是求取下标0~n的所有元素和,因为当某个元素change ( int i , int x ),就是第i元素加或减 x , 树状数组的每一个后继都会同样加或减 x ,那么我们可以在 i 的下一个坐标做相反的change();
例:change ( 2 , 2 )之后再change ( 2+1 ,  -2  );这样Sum(int n)函数得到的就是下表为 n 的元素值了。

来个例题巩固巩固: 士兵杀敌(四)



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值