目录
- 1 理论
- 2 实践
- 3 参考
1 理论
树状数组(Fenwick Tree),也称为二叉索引树(Binary Indexed Tree, BIT),是一种用于高效计算数组前缀和的数据结构。它可以在 O ( l o g N ) O(logN) O(logN)的时间复杂度下完成单点更新、前缀和查询等操作。
树状数组的主要思想是利用二进制的特性来进行有效的更新和查询。它的核心思想是利用数组的索引来表示树状结构,从而实现高效的查询和更新操作。
具体来说,树状数组的结构如下:
- 数组C:这是一个额外的数组,用于存储原始数组的前缀和或其它需要维护的信息。
- 每个元素的索引i代表了原始数组中的某个位置。
- 每个元素的值C[i]存储了原始数组中某个位置到其父节点(通过某种规则计算)的信息。通常,C[i]的计算规则是将i的二进制表示中最低位的1所代表的区间累加到i上。
lowbit(x)
表示数x
二进制表示中最后一位1
的大小,用十进制表示。
int lowbit(int x) {return x & -x;
}
例如输入6,返回2;输入7返回1;输入8返回8;输入9返回1。
对于数x
,加上lowbit(x)
的写法,
//写法1
x += lowbit(x);
//写法2
x += x & -x;
对于数x
,减去lowbit(x)
的写法,
//写法1
x -= lowbit(x);
//写法2
x -= x & -x;
//写法3
x &= x - 1;
模板题:307区域和检索-数组可修改
C++模板如下,
class Fenwick {vector<int> nums; //下标从1开始,nums[0]无意义vector<int> tree; //tree[i]表示第i个关键区间的元素和。也即数组nums中下标在[i-lowbit(i)+1, i]区间内的元素之和,它的元素个数为lowbit(i)。
public:inline int lowbit(int x) {return x & -x;}Fenwick(vector<int> nums) { this->nums = nums;int n = nums.size();for (int i = 1; i < n; ++i) {add(i, nums[i]); //下标从1开始}return;}//把nums数组中下标i的元素增加了x,更新对应的关键区间元素和tree[k]void add(int i, int x) {for (int k = i + 1; k < tree.size(); k += lowbit(k)) {tree[k] += x;}}//返回nums下标在[1,i]的元素之和,注意是闭区间int get_sum(int i) {int res = 0;for (int k = i; k > 0; k -= lowbit(k)) {res += tree[k];}return res;}//返回下标在[l,r]的元素之和,注意是闭区间int get_sum(int l, int r) {return get_sum(r) - get_sum(l - 1);}};
2 实践
题目1:3072. 将元素分配到两个数组中 II
C++代码如下,
class Fenwick {vector<int> tree;
public:Fenwick(int n) : tree(n) {} //下标从1开始//把下标为i的元素增加1void add(int i) {while (i < tree.size()) {tree[i]++;i += i & -i;}}//返回下标在[1,i]的元素之和,注意是闭区间int pre(int i) {int res = 0;while (i > 0) {res += tree[i];i &= i - 1;}return res;}
};class Solution {
public:vector<int> resultArray(vector<int>& nums) {auto sorted = nums;ranges::sort(sorted);sorted.erase(unique(sorted.begin(), sorted.end()), sorted.end());int m = sorted.size();vector<int> a{nums[0]}, b{nums[1]};Fenwick t1(m + 1), t2(m + 1); //t1,t2是树状数组t1.add(ranges::lower_bound(sorted, nums[0]) - sorted.begin() + 1); //+1是因为下标从1开始t2.add(ranges::lower_bound(sorted, nums[1]) - sorted.begin() + 1); //+1是因为下标从1开始for (int i = 2; i < nums.size(); ++i) {int x = nums[i];int v = ranges::lower_bound(sorted, x) - sorted.begin() + 1;int gc1 = a.size() - t1.pre(v);int gc2 = b.size() - t2.pre(v);if (gc1 > gc2 || gc1 == gc2 && a.size() <= b.size()) {a.push_back(x);t1.add(v);} else {b.push_back(x);t2.add(v);}}a.insert(a.end(), b.begin(), b.end());return a;}
};
题目2:
3 参考
- 带你发明树状数组!附数学证明(Python/Java/C++/Go/JS/Rust)