算法基础 - 树状数组(binary indexed tree)

定义

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

来源:百度百科

我把实现好的代码放到了我的github上:BIT 地址

基本概念

假设数组a[1..n],那么查询a[1]+…+a[n]的时间是log级别的,而且是一个在线的数据结构,支持随时修改某个元素的值,复杂度也为log级别。

下面讲一下整个的数组是如何实现的:

  1. 首先看图:
    树状数组图片
    图片来源见水印。

这里我们可以看到实际上每个节点存放的不一定是自己的值,其实是一段数组值的和。

C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
...
C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16

看到以上的这些了,C代表节点,后面代表这个节点是那些数据的和。
那么到底是哪些的和是怎么来的呢?

节点数值转换成二进制后,末尾有k个0,就有2的k次方个数据的和。

例如:
C1节点数字为1,二进制是:0001,末尾没有0,就是2的0次方,就是1,也就是只有一个数据的和。C1 = A1

如果是C2节点,数字为2,二进制: 0010,末尾一个0,就是2的1次方,就是两个数据的和。 C2 = A1+A2.

依次类推。

如何快速求末尾有几个0

这个就是树状数组的一个重要方法:

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

这样子就可以了。

求0到第n个数据的和

因为是树状数组,所以求和很简单,复杂度是log级别的。

int Sum = 0; // 第一步定义和为0
while(n > 0){
    Sum += C[n]; // 第二步 在 n > 0的情况 加上第n个节点
    n -= lowbit(n); //第三步 让n跳到上一个需要加的位置
}

是不是很简单!

更新数据

这里假如里面的某个数据更新了,那么有些节点也需要更新的,也是log级别的,第一个数据变动,就是需要log(n)其他的需要的更少。

//如果更新第i个数据,让其 +1
while(i <= n){ //一共n个数据,第0号节点无数据,所以下标到n
    C[i] += 1; // 更新数据
    i += lowbit(i); // 下一个要更新的位置
}

更新数据也非常简单。

大致如上。这是一个比较好的数据结构,我实现的代码暂时还有修改的余地和注释添加。后面我会完善。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值