数据结构-树状数组

懵懂的女人 提交于 2020-03-08 09:50:27

数据结构-树状数组

参考资料

暂无

树状数组是较堆功能更强大的能解决 \(\texttt{RMQ}\) 问题的数据结构 。

数组数组的前置知识:位运算

数组数组的功能:单点修改区间查询,区间修改单点查询(用差分)。

首先讲 \(lowbit(x)\),这是个位运算知识。表示 \(x\) 二进制下为 \(1\) 的最高位,如 \(lowbit((1110010)_2)=(10)_2\)\(lowbit((110000)_2)=(10000)_2\)\(\texttt{C++}\) 中的 \(x\&-x\) 正好能达到求 \(lowbit(x)\) 的效果。

然后树状数组中的 \(c\) 数组是个有跳跃性的前缀和数组,它通过 \(lowbit()\) 跳跃维护,然后通过 \(lowbit()\) 跳跃取值。原理较为复杂,不必深究。但是它有如下性质:

1.每次在 \(x\) 位置上 \(+y\) 时,可以这样维护 \(c[]\)

void fix(Tree&t,int n,int x,int y){
    for(;x<=n;x+=low(x)) t.c[x]+=y;
} 

2.如下方法即可求出 \(\Sigma_1^xa[i]\)

int fsum(Tree&t,int x){
    int res=0;
    for(;x;x-=low(x)) res+=t.c[x];
    return res;
}

所以树状数组的空间复杂度为 \(O(n)\),时间复杂度为 \(O(n\log n)\)

主要操作大概就是这样,那么蒟蒻就放代码了:

namespace Sumtree{
    int low(int x){return x&(-x);}
    class Tree{
    public:
        int c[N];
        Tree(){memset(c,0,sizeof(c));}
    }st;
    void fix(Tree&t,int n,int x,int y){// 在x位置上+y
        for(;x<=n;x+=low(x)) t.c[x]+=y;
    } 
    int fsum(Tree&t,int x){//求出x的前缀和
        int res=0;
        for(;x;x-=low(x)) res+=t.c[x];
        return res;
    }
}using namespace Sumtree;

有了前缀数组后,就可以单点修改区间查询了。如果想区间修改单点查询,就把每个 \(a[i]=a[i]-a[i-1]\) 然后每次修改修改区间两端的值,单点查询的时候求和。

比起线段树,树状数组功能较弱,但代码短个几十行。而且后面学树套树的时候,数组数组会好套很多。

祝大家学习愉快!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!