树状数组及其基本用途

树状数组

介绍:树状数组是一种时间复杂度为log(n),空间复杂度为o(n)的算法,可以高效计算数组的前缀和,区间和。

 

 

从图中我们可以观察到  

C1=A1

C2=A1+A2

C3=A3

C4=A1+A2+A3+A4

......

C数组就是我们要建的树状数组。

1.1建立树状数组

从图中可以看到从C1-C8,每一个节点都有自己管理范围(比如C4是管A1,A2,A3,A4,C5是管A5)。那么管理的范围如何确定,就要用到位运算

经过观察可以发现一个有趣的性质,对于编号为n的节点,这个节点管理的范围是2^k。(k就是n的二进制末尾为0的个数,如n=2,2在二进制中表示为10,有1个0,k就为1)

算2^k有一个好方法

int lowerbit(int n){//n为节点编号
    return n&-n;//返回值为2^k
}

现在设有一个有n个元素的数组a[n],要建一个树状数组c[n]

for(int i=1;i<=n;i++){
    int b=lowerbit(i);
    for(int j=i-b+1;j<=i;j++){//表示c[i]的管理范围从i-b+1到i,长度为lowerbit(i)
        c[i]+=a[j];
    }
}

 1.2树状数组的前缀和建立

复制代码
void add(int x, long long num) {
    while (x <= n) {
        tree[x] += num;
        x += lowbit(x);
    }
}
long long last = 0, now;
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &now);
        add(i, now - last);
        last = now;
    }
复制代码

 

这样建立那么,当对 x ~ y 的区间进行修改的时候需要在树状数组中的第 x 个位置 + k, 第 y + 1 个位置 -k

2.1树状数组的求和算法(1-n)

计算a[1]+a[2]+...a[n]

第一步:

  创建一个变量sum记入总和

int sum=0,n;//n是加到第几位

第二步:

  假如n<=0 结束循环,返回sum的值,否则sum = sum + c[n]

第三步:

  n-=lowerbit(n),使得n转到上一个范围(比如当n=6时,lowerbit(6)=2也就是范围是2,所以要6-2=4,所以n变为4,跳到c[4]的位置)

  然后继续第二步


int sum=0;
while(n>0){
    sum+=c[n];
    n-=lowerbit(n);
}//总代码

 

2.2树状数组的求和算法(a-b)

如果要求a[x]+a[x+1]+...a[y]

那么就用先算1到y的和,再算1到x-1的和,再相减

while(y>0){
    sum+=c[y];
    y-=lowerbit(y);
}
while(x>0){
    sum-=c[x];
    x-=lowerbit(x);
}

 

3.1树状数组的修改算法(给某点i加上x)

第一步:

  当i>n时,算法结束,不然就跳到第二步

第二步:

  c[i]+=x,i+=lowerbit(i),转到第一步使得i跳到下一个范围

while(i<=n){
    c[i]+=b;
    i+=lowerbit(i);
}

 

  

 

posted @   leojiang  阅读(79)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示