树状数组详解

导入

前缀和 相信大家都知道,它可以用来求区间和。

可是前缀和不怎么灵活,如果要对某个点进行修改,那就需要更新前缀和数组的所有下标不小于修改位置的元素的值,算法复杂度最高可达 O ( n ) O(n) O(n),速度十分缓慢,如果碰到大量修改,会直接歇菜。

为了加快单点修改的速度,树状数组就诞生了!

什么是树状数组

树状数组或二叉索引树(英语:Binary Indexed Tree),又以其发明者命名为Fenwick树,最早由Peter M. Fenwick于1994年以A New Data Structure for Cumulative Frequency Tables为题发表在SOFTWARE PRACTICE AND EXPERIENCE。其初衷是解决数据压缩里的累积频率(Cumulative Frequency)的计算问题(当我没说),现多用于高效计算数列的前缀和, 区间和。

以上内容来自网络。

如果你看完了上一段,那么恭喜你!浪费了10秒钟。

树状数组支持以下两种操作,

  • 单点修改。
  • 区间和查询。

两种操作的时间复杂度都为 O ( l o g   n ) O(log\space n) O(log n)

树状数组的构造

树状数组的思想大概是这样的,我们可以为数组维护多个区间,在查询和修改时,对这几个包含该点或区间的区间进行修改和查询。

可这些区间也不能随便设置,不然在进行查询修改时,如果包含区间的区间过多,那也会导致速度缓慢。

好了,不扯这些废话了,先来看看它长什么样吧。

设一个长度为 n n n 的树状数组 c i c_i ci,那它的元素维护的区间是这样的。


其中 c i c_i ci 就等于 a a a 数组 [ i − l o w b i t ( i ) + 1 , i ] [i-lowbit(i)+1,i] [ilowbit(i)+1,i] 的区间和。

l o w b i t ( i ) lowbit(i) lowbit(i) 是什么呢? l o w b i t ( i ) lowbit(i) lowbit(i) 指的是 i i i 在二进制下最后一个的为 1 1 1 的哪一位所代表的值。比如当 i = 10 i=10 i=10,即 ( 1010 ) 2 (1010)_2 (1010)2 时,最后一个为 1 1 1 的二进制位为从右往左第二位,所以 l o w b i t ( i ) = ( 10 ) 2 = 2 lowbit(i)=(10)_2=2 lowbit(i)=(10)2=2

lowbit的实现方法

l o w b i t ( x ) lowbit(x) lowbit(x) 的实现方法如下:

int Lowbit(int x){
   
	return x&(-x);
	//十分简洁,算法复杂度为 O(1)
}

为什么是 x&(-x) 呢?

大家应该都知道二进制的 原码、反码、补码。当 x x x 为负数时,他的二进制就是用补码来表示。即将 x x x 包括符号位取反后加 1 1 1

而将 x x x 变成负数后,二进制在运算时就会变成补码。因为 l o w b i t ( x ) lowbit(x) lowbit(x) 是最后一个为 1 1 1 的二进制位,所以它后面所有的二进制位都为 0 0 0,取反后自然为 1 1 1 l o w b i t ( x ) lowbit(x) lowbit(x) 的那一位则会变成 0 0 0,加 1 1 1 后, l o w b i t ( x ) lowbit(x) lowbit(x) 后面的二进制位会一直进位到 l o w b i t ( x ) lowbit(x) lowbit(x) 这一位,并全部变为 0 0 0。所以 l o w b i t ( x ) lowbit(x) lowbit(x) 的后面的二进制位进行按位与后都为 0 0 0。而 l o w b i t

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值