树状数组

树状数组是一种数据结构,用于高效地查询和修改数列元素。它可以在log(n)的时间复杂度内完成区间和查询及单点修改,常用于竞赛编程。与线段树类似但更高效,适用于区间修改和单点查询。本文介绍了树状数组的基本概念、Lowbit函数、Update和Getsum函数的实现。
摘要由CSDN通过智能技术生成

树状数组

树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。
这种数据结构(算法)并没有C++和Java的库支持,需要自己手动实现。在Competitive Programming的竞赛中被广泛的使用。树状数组和线段树很像,但能用树状数组解决的问题,基本上都能用线段树解决,而线段树能解决的树状数组不一定能解决。相比较而言,树状数组效率要高很多。
——摘自<树状数组>百度百科

设有一数列A{1…n}
用树状数组T[n]进行保存
Tn指数组第n个元素的数值,An指数列的第n个元素
在这里插入图片描述
如图所示

对于第n个数组元素Tn,它包含的数列元素为2^k个(其中k为n二进制末尾0的个数)
同时Tn被它后面第2^k个数组元素所包含。
比如T1就被它后面第2^k=1个数组元素,也就是T2所包含
而T2就被它后面第2^k=2个数组元素,也就是T4所包含

Lowbit函数

Lowbit函数的作用是求2^k
我们知道,二进制数的第n位(从右往左数起)表示的数值是2^(n-1)
当n为0001(十进制1)的时候,k为0,2^k为1
当n为0010(十进制2)的时候,k为1,2^k为2
当n为0011(十进制3)的时候,k为0,2^k为1
当n为0100(十进制4)的时候,k为2,2^k为4
……
可以发现,2^k的值等于n最低位1所代表的数值
所以,Lowbit函数的作用也是取出n最低位的1

在计算机中,使用补码储存和表示数值
补码的负数相当于原码取反再加1
而补码和原码进行按位与运算时(&),得到的结果就是原码最低位1
ps:按位与运算(&)同是1为1,否则为0

因此Lowbit可以这样写

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

还有另一种写法
这里先给出,但是具体思路还不清楚,只知道作用是相同的

int Lowbit(int x){
	return x&(x^(x-1));//^:按位异或,不同为1,相同为0
}

修改数列元素An的函数Update

若要修改An,必须修改其所有祖先,最坏情况下为修改第一个元素,最多有log2(n)个的祖先
由于An被从Tn开始的多个数组元素包含,因此需要循环添加直到树状数组边界
上面也有提到,Tn被它后面第2^k个数组元素所包含
以下是代码:

void update(int n,int number){//n为修改的数列元素Ax,number为改变数值
	while(n<=along){//along为树状数组长度
		T[n]+=number;
		n+=Lowbit(n);//往后移动到下一个包含Ax的数组元素
	}
}

获得数列A1…An和的函数Getsum

由于A1…An的和被分割为许多块,因此需要一块块加起来才能得到结果
上面也有提到,Tn包含的数列元素数为2^k,即Lowbit(n),而从A1到An一共包含n个数列元素
可以这么理解,从Tn开始,每次获得Lowbit(当前格)个元素,因此要向前跨Lowbit(当前格)格,直到走到T1之前(获得所有元素)。
以下是代码:

int Getsum(int n){
	int sum=0;//sum为A1…An的和
	while(n>0){
		sum+=T[n];//添加当前的一块
		n-=Lowbit(n);//往前跨Lowbit(n)格
	}
	return sum;
}

n –= Lowbit(n)实际上等价于将n的二进制的最后一个1减去。而n的二进制里最多有⌈log2(n)⌉(向上取整)个1,所以查询效率是log2(n)的。

==============
以求A1…A7的和为例
得出结果为T7+T6+T4只要三个数相加
而如果用普通数组直接保存数列A,要求A1…A7的和需要七个数相加;如果普通数组保存A的前n项和,每次修改An都要改动n个数组元素

因此在需要多次求数组某一段的和时,使用树状数组保存可以有效提高效率

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值