Fenwick Tree(Binary Indexed Tree) - 树状数组(二进制索引树)


树状数组(二进制索引树)

给定个数字,求区间(其中)上的所有成员之和(简称区域和/区间和)

其中。该区间可以修改任意元素的值。

区间上任意位置的值可以修改,但所有元素都恒为非负整数。

循环累加数组的算法和Segment Tree算法都可以解决该问题,Fenwick树(树状数组/二进制索引树)与Segment Tree同样在时间复杂度内求出某个区域上的和,但空间复杂度更低。实际上由于字节操作,FenwickTree不但占用内存少,且速度更快(内存更紧凑的软件速度更快)。

,那么的区间和可以表示为。本问题可以转化为求区间上前个元素之和的问题。

任意非负整数都可以表示为的次方和,比如。所以任意非负整数可以用一个代表bit的数组表示:,即二进制数字。那么区域可以用个二进制数组来表示所有数字,用Fenwick树结构来表示:

FenwickTree1.png

引入最低有效位lowbit函数来构造Fenwick树上的每个节点。是一个非负整数的二进制数字中,最低的非位的数字(比如,显然奇数有):

FenwickTree2.png

令Fenwick树上的节点存储值

比如

那么Fenwick树上每个节点覆盖到的区域和如图所示:

FenwickTree4.png

设数字的次方和表示为:

比如。那么恰好有

求非负整数的最低有效位分为以下几步:

使的最低有效位变为0(),最低有效位之前的那些变为,比如

然后异或操作()就可以将的最低有效位及之前的所有位设置为,比如

最后与原做与操作(),结果即为最低有效位对应的数字;

lowbit函数的C++实现如下

int LowBit(int x) {
    return x & (x ^ (x-1));
}

或者利用反码特性实现

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

对于长度为的区域,构造Fenwick树的时间复杂度为,查询区域和的时间复杂度为,修改区域上一个值的时间复杂度为,空间复杂度为


二进制索引树


源码

FenwickTree.h

FenwickTree.cpp

测试

FenwickTreeTest.cpp