树状数组解析

目录

一、基本概念

二、单点修改

三、区间查询


一、基本概念

现在有一个长度为 n 的数组a[],有两种操作:  

1、求区间 [1,x] 的和  

2、更改 a[x] 的值

这个问题怎么解决?

暴力:对于求和:直接遍历求和,对于更改:直接修改 a[x] 的值。这样的时间复杂度是多少?每次求和:O(n);每次修改:O(1),如果有大量的求和,每次O(n)的求和 时间复杂度就很高。那我们应该如何去优化呢?这就要提到树状数组了。

我们用一个树形的结构来表示数组,用叶子节点存值,可以得到下面这张图:

 用 c[] 来表示子树的叶子结点的权值之和,c[] 既可以表示出单个点的值a[i],又可以表示和,而且可以用数组写出。

那么c数组的下标该如何表示呢?

转换成二进制,我们可以发现规律:

 

 我们不难看出,与二进制数末尾的0个数有关。如果我们设末尾有k个0,可以得出以下公式:

末尾有多少个0,其实就是从低位开始的首个1在哪。我们可以通过下面这个函数得出从低位开始首个1的位置,即2^k的值:

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

 原理也不难,在此不多加赘述,感兴趣者可自行查阅。

二、单点修改

单点修改并不难,一张示意图基本就能搞明白:

即:修改x位置的值,需要一并修改的有x+lowbit(x),一直这样修改直到修改到最后一个。

代码:

void update(int x,int y){  //x为更新的位置,y为变化的量
    for(int i=x;i<=n;i+=lowbit(i)){
        c[i]+=y;
    }
}

三、区间查询

我们可以利用c数组来求a数组前n项和,如n=7时:

sum[7]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]    

=(a[1]+a[2]+a[3]+a[4])+(a[5]+a[6])+a[7]    

=c[4]+c[6]+c[7]

写成二进制:sum[(111)]=c[(100)]+c[(110)]+c[(111)];

有什么规律?

那么我们就可以得到区间查询的代码:

int getsum(int x){
    int ans=0;
    for(int i=x;i;i-=lowbit(i)){
        ans+=c[i];
    }
    return ans;
}

 这就是树状数组的基本内容,它的练习题我就不放了,大家可以自行上网寻找,不懂的地方可以看题解。谢谢观看!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值