参考
https://www.geeksforgeeks.org/binary-indexed-tree-or-fenwick-tree-2/
实际问题(简单记,想快速知道数组的前缀和,同时数组的修改频繁,用树状数组,使得这两种操作都变成O(logn),两边查询的树状数组可以相当于线段树的功能)
We have an array arr[0 . . . n-1]. We would like to 1 Compute the sum of the first i elements. 2 Modify the value of a specified element of the array arr[i] = x where 0 <= i <= n-1.
A simple solution is to run a loop from 0 to i-1 and calculate the sum of the elements. To update a value, simply do arr[i] = x. The first operation takes O(n) time and the second operation takes O(1) time.
Another simple solution is to create an extra array and store the sum of the first i-th elements at the i-th index in this new array. The sum of a given range can now be calculated in O(1) time, but the update operation takes O(n) time now. This works well if there are a large number of query operations but a very few number of update operations.
Could we perform both the query and update operations in O(log n) time? One efficient solution is to use Segment Tree that performs both operations in O(Logn) time.
An alternative solution is Binary Indexed Tree, which also achieves O(Logn) time complexity for both operations. Compared with Segment Tree, Binary Indexed Tree requires less space and is easier to implement..
给定一个数组[0, .., n-1],我们想:
- 计算出前i个元素的和
- 将任何一个元素改为x,arr[i] = x where 0<=i<=n-1
和线段树的实际问题的区别在于,线段树是从l到r任何一段,树状数组只是前i个。
同样第一个简单的解法就是循环求和O(n),改变为O(1)。
或者创建一个新数组的每个元素存的前i个数的和,这样求和就是O(1),但是改变后数组就要调整O(n)的时间复杂度。
Representation Binary Indexed Tree is represented as an array. Let the array be BITree[]. Each node of the Binary Indexed Tree stores the sum of some elements of the input array. The size of the Binary Indexed Tree is equal to the size of the input array, denoted as n. In the code below, we use a size of n+1 for ease of implementation.
Construction We initialize all the values in BITree[] as 0. Then we call update() for all the indexes, the update() operation is discussed below.
Operations
1 | getSum(x): Returns the sum of the sub-array arr[0,...,x] |
The diagram above provides an example of how getSum() is working. Here are some important observations.
BITree[0] is a dummy node.
BITree[y] is the parent of BITree[x], if and only if y can be obtained by removing the last set bit from the binary representation of x, that is y = x – (x & (-x)).【index&(index-1) 也可以】
The child node BITree[x] of the node BITree[y] stores the sum of the elements between y(inclusive) and x(exclusive): arr[y,…,x).
1 | update(x, val): Updates the Binary Indexed Tree (BIT) by performing arr[index] += val |
The update function needs to make sure that all the BITree nodes which contain arr[i] within their ranges being updated. We loop over such nodes in the BITree by repeatedly adding the decimal number corresponding to the last set bit of the current index.
How does Binary Indexed Tree work? The idea is based on the fact that all positive integers can be represented as the sum of powers of 2. For example 19 can be represented as 16 + 2 + 1. Every node of the BITree stores the sum of n elements where n is a power of 2. For example, in the first diagram above (the diagram for getSum()), the sum of the first 12 elements can be obtained by the sum of the last 4 elements (from 9 to 12) plus the sum of 8 elements (from 1 to 8). The number of set bits in the binary representation of a number n is O(Logn). Therefore, we traverse at-most O(Logn) nodes in both getSum() and update() operations. The time complexity of the construction is O(nLogn) as it calls update() for all n elements.
1 | # 树状数组binary-indexed-tree.py |
Output:
1 | Sum of elements in arr[0..5] is 12 |
Can we extend the Binary Indexed Tree to computing the sum of a range in O(Logn) time? Yes. rangeSum(l, r) = getSum(r) – getSum(l-1).
Applications: The implementation of the arithmetic coding algorithm.
典型题目
leetcode 315 计算右侧小于当前元素的个数
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例:
输入:[5,2,6,1] 输出:[2,1,1,0] 解释: 5 的右侧有 2 个更小的元素 (2 和 1) 2 的右侧仅有 1 个更小的元素 (1) 6 的右侧有 1 个更小的元素 (1) 1 的右侧有 0 个更小的元素
链接:https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self
树状数组解法,用每个数的rank来离散化,反序插入树状数组中,前一位的前缀和就是右侧小于当前元素的个数。
示例代码如下:
1 | class Solution: |