前缀和/树状数组
前缀和/树状数组

前缀和/树状数组

前缀和数组:

初始化:O(n) 时间复杂度,顺序扫描原数组即可

查询区间和:O(1) 时间复杂度,S[j] – S[i] 即为原数组i到j的区间和

单点修改:O(n) 时间复杂度,需要修改S[i] ~ S[n]所有值

慢,是因为S[i]的值与之前原数组中所有项都有关系

弱化这种关系,即可加快单点修改速度,当然也会丧失部分查询速度。

可这种取舍,是值得的!

lowbit函数

lowbit(i):代表i这个数字,二进制表示的最后一位1的位权

例如:

  • lowbit(8) = (1000)2 = 8
  • lowbit(6) = (0110)2 = 2
  • lowbit(12) = (1100)2 = 4
  • lowbit(7) = (0111)2 = 1

lowbit(x) = x & (-x)

树状数组

改进前缀和:

lowbit(i):C[i]代表 第i位 的前lowbit(i)项 之和。

例如:

lowbit(10) = 2, C[10] = a[10] + a[9]

lowbit(12) = 4, C[12] = a[12] + a[11] + a[10] + a[9]

前缀和查询:S[i] = S[i – lowbit(i)] + C[i]

例如:

S[7] = S[6] + C[7] = S[4] + C[6] + C[7] = C[4] + C[6] + C[7]

S[12] = S[8] + C[12] = C[8] + C[12]

单点修改:当修改A[j]位置的值的时候,首先需要更新的显然是C[j]的值,可C[j]之后,应该更新哪个值呢?也就是找到C[j]脑袋上面的区间。

要更新的下一个值是:C[i + lowbit(i)]

例如:

更新原数组A[5]的值,那么需要更新C[5],C[6],C[8]这三个点的值

代码实现:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <string>
#include <map>
#include <set>
#include <vector>
using namespace std;

#define lowbit(x) (x & (-x))
class FenwickTree {
public :
    FenwickTree(int size) : n(size), c(n + 1) {}

    // 对原数组进行单点增加 第i位的值增加x
    void add(int i, int x) {
        while (i <= n) c[i] += x, i += lowbit(i); // 向上更新
        return ;
    }

    // 查询原数组 ind位置的值
    int at(int ind) { return query(ind) - query(ind - 1); }

    // 查询原数组 x位置前缀和 
    int query(int x) {
        int sum = 0;
        while (x) sum += c[x], x -= lowbit(x);
        return sum;
    }

    // 测试输出
    void output() {
        int len = 0;
        for (int i = 1; i <= n; i++) {
            len += printf("%5d", i); // 打印坐标位
        }
        printf("\n");
        for (int i = 0; i < len + 6; i++) {
            printf("=");
        }
        printf("\n");
        for (int i = 1; i <= n; i++) {
            printf("%5d", c[i]); // 打印树状数组 
        }
        printf("\n");
        for (int i = 1; i <= n; i++) {
            printf("%5d", query(i) - query(i - 1)); // 打印原数组
        }
        printf("\n\n\n");
        return ;
    }

private:
    int n; // 下标上限
    vector<int> c; // 树状数组
};

int main() {
    int n;
    cin >> n;
    FenwickTree tree(n);
    for (int i = 1, a; i <= n; i++) {
        cin >> a;
        tree.add(i, a);
    }
    tree.output();
    int ind, val;
    while (cin >> ind >> val) {
        cout << "change " << ind << " to " << val << endl;
        tree.add(ind, val - tree.at(ind)); // 将原数组ind位置的值 改为val
        tree.output();
    }
    return 0;
}

发表评论

邮箱地址不会被公开。