树状数组--动态维护区间操作

树状数组 (二元索引树 / 二元下标树 / 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;
        }
    }
};
复制代码

 

posted @   QianFa01  阅读(105)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示