引言
LeetCode
307这道题给一个可修改的数组,需要进行频繁的区域和检索。
一个naive的做法是,每次查询都从\(i\)累加到\(j\): 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class NumArray {
public:
NumArray(vector<int>& nums) {
nums_ = nums;
}
void update(int i, int val) {
nums_[i] = val;
}
int sumRange(int i, int j) {
int ans = 0;
for (int k = i; k <= j; ++k)
ans += nums_[k];
return ans;
}
private:
vector<int> nums_;
};
树状数组
树状数组可以在\(O(lgn)\)时间复杂度内完成上述两个操作:
- 单点更新
- 计算前缀和并进行区间查询
BIT并不需要定义树的结点和指针,而是维护了一个特殊的前缀和数组prefix_sum_
,下面的例子均是1-indexed,调用BIT时需要传入原始索引+1。填充prefix_sum_
的过程是这样的:
1.
按照索引的二进制表示,先看最低位,将所有最低位为1的数值直接存入T 2.
再看次低位为1的(即10结尾的),将该数和前一个数(共2个数)的和存入T 3.
再看以100结尾的,将该数及之前的3个数(共4个数)的和存入T 4.
再看以1000结尾的,将8个数的和存入T,以此类推...
填充好prefix_sum_
后,就可以查询原始数组的前缀和并且更新原始数组。
查询
假设要求前缀和A[1]+...+A[7]即query(7)
,只需要query(7)+query(6)+query(4)
即可,从二进制来看就是query(00111)+query(00110)+query(00100)
,即每次将最后一位1翻转然后累加直到i变为0。
更新
假设要更新A[4]即update(4, 10)
,需要更新T[4]和T[8],即00100
和01000
,即每次将最后一位1左移直到i超出数组长度,移位过程中更新相应的T[i]。
填充prefix_sum_
可以直接调用update
。
那么我们的tree: 0是dummy node,将结点的二进制表示的最后一个1翻转,就能得到其父结点。
下来填充这棵树: \(1=0+2^0\),存储从下标0开始的前1个数的和:3(0,0);
\(2=0+2^1\),存储从下标0开始的前2个数的和:5(0,1);
\(3=2^1+2^0\),存储从下标2开始的前1个数的和:-1(2,2);
\(4=0+2^2\),存储从下标0开始的前4个数的和:10(0,3);
\(5=2^2+2^0\),存储从下标4开始的前1个数的和:5(4,4);
\(6=2^2+2^1\),存储从下标4开始的前2个数的和:9(4,5);
\(7=2^2+2^1+2^0\),存储从下标6开始的前1个数的和:-3(6,6);
\(8=0+2^3\),存储从下标0开始的前8个数的和:19(0,7);
\(9=2^3+2^0\),存储从下标8开始的前1个数的和:7(8,8);
\(10=2^3+2^1\),存储从下标8开始的前2个数的和:9(8,9);
\(11=2^3+2^1+2^0\),存储从下标10开始的前1个数的和:3(10,10);
填充后的tree:
接下来就可以根据这棵树来计算prefixSums_
: 假如要计算\(0-5\)的和,从下标6出发,一直加到dummy
node,得到prefixSums_[6]=9+10=19
; 要计算\(0-9\)的和,从下标10出发,一直加到dummy
node,得到prefixSums_[10]=9+19=28
。 以计算\(0-9\)的和为例,结点10存储的是(8,9)的部分和,结点8存储的是(0,7)的部分和,所以加起来就是\(0-9\)的和。
快速实现
上面求结点的父结点、将下标拆解为二进制去填充树的方式很慢,来看一种稍快的方式。
查询时,我们需要计算从某结点到dummy
node的和,这就涉及计算该结点的parent:
假如要求结点7的parent,7的二进制原码为111
,-7的补码为001
,将原码和补码按位与得001
,用原码减去001
,得110=6
,即7的父结点是6。
更新时,我们需要更新所有包含该结点的部分和结点:
假如更新了结点1,1的二进制原码为001
,-1的补码为111
,将原码和补码按位与得001
,用原码加上001
,得010=2
,即还要更新结点2,更新了结点2,还要更新结点4......
最后来看下非常简洁的实现:
1 | // 一维 |
1 | class NumMatrix { |
Reference
Fenwick Tree or Binary Indexed
Tree
花花酱
Fenwick Tree / Binary Indexed Tree SP3
Fenwick Tree