树状数组--动态维护区间操作
树状数组 (二元索引树 / 二元下标树 / Binary Indexed Tree, BIT / Fenwick Tree): 树状数组虽名为数组,但从其英文名 (Binary Indexed Tree) 可看出它本质上是一种被表达为树的数据结构。对于大小为n 的序列nums ,最基本的树状数组以O(logn) 时间复杂度同时支持如下两种操作。
1)更新nums 中单个元素的值,即 单点修改(Point Update) 。
2)求nums 任意区间的元素值之和,即 区间查询(Range Query) 。
数组:对于单点修改:数组可以利用下标直接修改,O(1),但是对于区间查询则为O(N);
前缀和:对于区间查询,前缀和可以做到O(1),但是对于单点修改,需要修改该点以后的所有前缀和数值,O(N);
图片摘自:〔manim | 算法 | 数据结构〕 完全理解并深入应用树状数组 | 支持多种动态维护区间操作_哔哩哔哩_bilibili
代码摘自:树状数组从入门到下车 - 力扣(LeetCode)
lowbit操作:
t [ ] 数组:
单点修改、区间查询:
初始化tree 数组:
将tree数组的大小设置为 nums数组的大小,初值为0;然后循环调用 add函数 执行 add(i, nums[i]);初始化操作时间复杂度O(N logN);空间复杂度为O(N);
单点修改、区间查询的时间复杂度均为 O(logN);
类代码实现:
#include <bits/stdc++.h> using namespace std; class PURQbit { private: vector<int>tree; vector<int>nums; int n; int lowbit(int i){ return i & -i; } void add(int index, int x){ for(int i = index + 1; i <= n; i += lowbit(i)){ tree[i-1] += x; } } int presum(int index){ int res = 0; for(int i = index + 1; i > 0; i -= lowbit(i)){ res += tree[i-1]; } return res; } public:
PURQbit(){} PURQbit(vector<int>& nums) { this->nums = nums; this->n = nums.size(); tree.resize(n , 0); for(int i = 0;i < n; ++i){ add(i, nums[i]); } } void update(int index, int val) { add(index, val - nums[index]); nums[index] = val; } int sumRange(int left, int right) { return presum(right) - presum(left - 1); } }; /** * Your NumArray object will be instantiated and called as such: * NumArray* obj = new NumArray(nums); * obj->update(index,val); * int param_2 = obj->sumRange(left,right); */
区间修改、单点查询:
代码实现:利用上述单改区查
class RUPQbit { private: PURQbit purq; public: RUPQbit(vector<int>& nums){ int n = nums.size(); vector<int>diffs(n); diffs[0] = nums[0]; for(int i = 1;i < n; ++i){ diffs[i] = nums[i] - nums[i-1]; } this->purq = PURQbit(diffs); } void update(int Left, int right, int x){ purq.add(Left, x); purq.add(right+1, -x); } int query(int index){ return purq.sumRange(0, index); } };
区间修改、区间查询:
整个矩形面积,减去黄色面积;
注意这儿是 前缀和的增量;
代码实现:
class RURQbit{ private: vector<int>diffs, tree, aidtree; int n; int lowbit(int i){ return i & -i; } public: RURQbit(vector<int>& nums){ int n = nums.size(); this->diffs.resize(n); this->tree.resize(n); this->aidtree.resize(n); diffs[0] = nums[0]; for(int i =1; i < n; ++i){ diffs[i] = nums[i] - nums[i-1]; } for(int i = 0;i < n;++i){ add(tree,i, diffs[i]); add(aidtree,i, i*diffs[i]); } } void update(int left, int right, int x){ add(tree,left,x); add(tree,right+1,-x); add(aidtree,left,left*x); add(aidtree,right+1,(right+1)*(-x)); } int query(int left, int right){ int preleft = left * presum(tree, left-1) - presum(aidtree, left-1); int preright = (right+1) * presum(tree,right) - presum(aidtree, right); return preright - preleft; } int presum(vector<int>&tree, int k){ int res = 0; for(int i = k+1; i > 0; i -= lowbit(i)){ res += tree[i-1]; } return res; } void add(vector<int>& tree, int k, int x){ for(int i = k + 1;i <= n; i += lowbit(i)){ tree[i-1] += x; } } };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具