树状数组及其基本用途
树状数组
介绍:树状数组是一种时间复杂度为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); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】