数据结构和算法 - 树状数组

数据结构与算法 - 树状数组

1. 问题


序号 题目 难度
—— ————————————————————————————————————————————— ————
307. 区域和检索 - 数组可修改 中等
315. 计算右侧小于当前元素的个数 困难
493. 翻转对 困难
面试题 10.10. 数字流的秩 中等
HDU P1166 敌兵布阵

给定一个数组\(A\),长度为\(n\),数组的下标范围是\([0,n-1]\),对数组A可进行:

  1. 查询\([i,j]\)区间内的和;
  2. 对位置\(i\)的值,加一个值\(x\)

常规解决思路:抽象为“区间和查询”和“区间更新”问题,

  1. 模拟,操作1的时间复杂度是\(O(n)\),操作2的时间复杂度是\(O(1)\);
  2. 前缀和,操作1的时间复杂度是\(O(1)\),操作2的时间复杂度是\(O(n)\);

2. 定义

为了平衡操作1和操作2,引进了树状数组\(C\),即每个位置i表示原始数组的一个区间,值\(C[i]\)表示的是原始数组\(A\)\([i-lowbit(i)+1, i]\)区间之和,其中\(lowbit(i)\) 表示数字i的二进制表示中,位数最低的1所代表的值,如:

\[\begin{array}{l} 1 -> [0] \\ 2 -> [1,2] \\ 3 -> [3] \\ 4 -> [1,2,3,4] \\ i -> [i-lowbit(i)+1, i] \\ \end{array} \]

个人理解:最低位的1的位置表示区间的长度,如\((1000)_{2}\)表示\([1,8]\)的区间。

3. 操作

  1. 二进制位
  2. 区间添加
  3. 区间查询

class TreeArray{
    
    vector<int> tr;
    vector<int> nums;
    int n;
    
    int lowBit(int x){
        return x & -x;
    }
    void add(int x, int u){
        for(int i=x; i<=n; i+=lowBit(i)){
            tr[i] += u;
        }    
    }
    int query(int x){
        int ans = 0;
        for(int i=x; i> 0; i -= lowBit(i)){
            ans += tr[i];
        }
        return ans;
    }
    
    // 初始化
    // for(int i=0; i< n;i++){
    //    add(i+1, nums[i]);
    //}
    
    // 区间和
    // sumRange: query(right+1) - query(left);
};

4. 复杂度

时间复杂度:\(O(\log(n))\)

空间复杂度:\(O(n)\)

5. 拓展:最值问题

Link: https://www.cnblogs.com/iwtwiioi/p/3869868.html

https://www.cnblogs.com/mypride/p/5002556.html

含义:\(C[x]\)表示\([x-lowbit(x)+1, x]\)区间最值;

更新:最值操作,划分区间为\([x-lowbit(x)+1, ..., x-2^2, x - 2^1, x - 2 ^ 0]\),依次求取最值;

查询:对于区间和,只需要利用前缀和相减即可,但是最值问题并不行。根据定义\(C[right]\)表示\([right-lowbit(right)+1, right]\), 并比较\(right-lowbit(right)+1\)\(left\)大小,

void update(int x, int val)
{
    C[x] = val;
    for(int i=1; i<lowbit(x); i=i<<1){
        C[x]=max(C[x], C[x-i]);
    }
}
int query(int x)
{
    int ret = INT_MIN; 
    while(x > 0){
        ret = max(ret, C[x]);
        x -= lowBit(x)
    }
    return ret;
} 

int query(int left, int right)
{
    int ret = nums[right];
  
    while(left <= right) {
        ret=max(ret, num[right]);
   
        for(--right; right - left >= lowbit(right); r -= lowbit(right)){
            ret = max(ret, C[right]);
        }
            
    }

	return ret;
} 
posted @ 2022-04-04 22:49  yangly1  阅读(23)  评论(0)    收藏  举报