树状数组(Binary Indexed Tree),看这一篇就够了

定义

根据维基百科的定义:

A Fenwick tree or binary indexed tree is a data structure that can efficiently update elements and calculate prefix sums in a table of numbers.

也就是说,所谓树状数组,或称Binary Indexed Tree, Fenwick Tree,是一种用于高效处理对一个存储数字的列表进行更新及求前缀和的数据结构。

举例来说,树状数组所能解决的典型问题就是存在一个长度为n的数组,我们如何高效进行如下操作:

  1. update(idx, delta):将num加到位置idx的数字上。
  2. prefixSum(idx):求从数组第一个位置到第idx(含idx)个位置所有数字的和。
  3. rangeSum(from_idx, to_idx):求从数组第from_idx个位置到第to_idx个位置的所有数字的和

对于上述问题,除去每次求和都对原数组相关数字暴力相加求和的解法外,另一种较简单解法为使用O(n)时间构造一个_前缀和数组(cumulative sum)_,即该数组中的第i个位置保存原数组中前i个元素的和,则对于上述每一个操作,我们有:

  1. update(idx, delta):更新操作需要更新cumulative sum数组中每一个受此更新影响的前缀和,即从idx其到最后一个位置的前缀和。该操作为O(n)时间复杂度。
  2. prefixSum(idx):直接返回cumulativeSum[idx + 1]即可。该操作为O(1)时间复杂度。
  3. rangeSum(from_idx, to_idx):直接返回cumulativeSum[to_idx + 1] - cumulativeSum[from_idx]即可。该操作为O(1)操作。

可以看出,该简单解法的求和操作非常高效,而单个更新操作为线性时间。如果所需的更新操作的数量远少于求和操作的话,该解法非常合适。反之,如果更新操作较多,我们就需要思考优化的方法。

那么使用树状数组解决该问题的目的就是为了在保证求和操作依然高效的前提下优化update(idx, delta) 操作的时间复杂度。

填坑法构造Binary Indexed Tree

所谓的Binary Indexed Tree,首先需要明确它其实并不是一棵树。Binary Indexed Tree事实上是将根据数字的二进制表示来对数组中的元素进行逻辑上的分层存储。

Binary Indexed Tree求和的基本思想在于,给定需要求和的位置i,例如13,我们可以利用其二进制表示法来进行分段(或者说分层)求和:13 = 2^3 + 2^2 + 2^0,则prefixSum(13) = RANGE(1, 8) + RANGE(9, 12) + RANGE(13, 13) (注意此处的RANGE(x, y)表示数组中第x个位置到第y个位置的所有数字求和)。如下面例子中所示:

arr = [1, 7, 3, 0, 5, 8, 3, 2, 6, 2, 1, 1, 4, 5]
prefixSum(13) = RANGE(1, 8) + RANGE(9, 12) + RANGE(13, 13)
= 29 + 10 + 4 = 43

那么如果我们将上述的range sum提前计算好的话,prefixSum(13)可以直接由它们相加得到。那么我们所需要解决的问题就是,根据何种规则来计算和存储这样的二进制表示后所需的range sum呢?规则如下图中所示。

Binary ranges

图中第一行为原数组,第二到第四行为依次按层填坑的过程。我们需要从左到右,从上到下依次将相应的值填入对应的位置中。最后一行中即为最终所形成的树状数组。

以图中第二行,也就是构造树状数组第一层的过程为例,我们首先需要填充的是数组中第一个数字开始,长度为__2的指数__个数字的区间内的数字的累加和。所以图中分别填充了从第一个数字开始,长度为2^0, 2^1, 2^2, 2^3的区间的区间和。到此为止这一步就结束了。因为2^4超过了我们原数组的长度范围。

下一步我们构造数组的第二层。与上一层类似,我们依然填充余下的空白中从第空白处一个位置算起长度为__2的指数__的区间的区间和。例如3-3空白,我们只需填充从位置3开始,长度为1的区间的和。再如

  • 71
    点赞
  • 118
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

耀凯考前突击大师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值