树状数组和线段树

树状数组与线段树

知识来源:5.1 树状数组和线段树 - AcWing

对比

1.线段树组的适用范围包含树状数组的适用范围,即树状数组可以解的题线段树可以解决,反之则不然。

2.树状数组也有优势:代码短,运行效率高

3.两种结构下标都是从1开始

树状数组 O(logn)

可以解决的问题:

  • 使某个位置上的数加上一个数 单点修改
  • 求某一个点的前缀和 区间查询

本身树状数组只支持上述两种操作,但是结合差分的思想可以完成:单点查询,区间修改等

--如果只有查询那就用前缀和,如果还有单点修改那就树状数组。

注意其中:

1.lowbit(x)的定义如图所示。

2.c[x]=(x-lowbit(x),x] 其含义是c[x]计算的是下标从x-lowbit(x)到x左开右闭区间,包右不包左

3.C、A两个数组下标最好从1开始。

代码模板:

设A数组为原数组,C数组为计算出的树状数组,那么:

单点修改:

原数组a[x]+=v,那么c数组的更新

fori(int i=x;i<=n;i+=lowbit(x)){
	c[i]+=v;
}

区间查询:

对于一段区间[1,x]那么c[x]的值如下:

那么要计算[1,x]区间和:

for(int i=x;i>0;i-=lowbit(x)){
	res+=c[i];
}

例题:

AcWing 1264. 动态求连续区间和

线段树 O(logn)

可以解决的问题:

  • 使某个位置上的数加上一个数 单点修改

  • 求某一个区间的xxx 区间查询

    注意线段树是只能用来计算前缀和,但是线段树可不只能动态计算前缀和

    如果想完成 区间修改、单点查询等操作,可能设计线段树的懒标记操作,由于难度过大,蓝桥杯等级的比赛基本不会涉及,因此目前阶段只考虑单调修改和区间查询。

核心函数

由于结果和树状数组不同,因此要实现单点修改和区间查询操作,线段树主要设计4个核心函数。

下面的伪代码是以计算区间最大值为模板的,需要计算其他值需要相应的修改

①pushup:用子节点信息更新当前结点信息

void pushup(int u){
    tr[u].max=Math.max(tr[u<<1].max+tr[u<<1|1].sum);
}
//u代表子节点的下标(两个树的下标都从1开始的),且线段树的四种核心函数都带参数u
//线段树里面存放的是自定义的结构体,如果是最小值里面有三个参数:l、r、max,其中l,r值得是原序列的下标l,r
//u<<1等价于u/2舍尾,即左二子;u<<1|1等价于u/2+1,即右儿子

②build:在一段区间上初始化线段树

void build(int u,int l,int r){
    if(l==r){
        //到了最底部的结点了
        tr[u]={l,r,w[r]};//w就是原数组
    }else{
        tr[u]={l,r};
        int mid=l+r >>1;
        //还没到最底部结点,于是先建立下面的结点,最后在pushup(u)算出此结点的值
        build(u<<1,l,mid);build(u<<1|1,mid+1,r);//从这就可以看出来构造线段树的时候mid属于左边,mid+1属于右边
        pushup(u);
    }
}

③modify:修改

void modify(int u,int x,int v){
	if(tr[u].l==tr[u].r){
        tr[u].max=v;
    }else{
        int mid=tr[u].l+tr[u].r;
        //修改是左右选合适的一个走,build是左右各走一遍。
        if (x <= mid) modify(u << 1, x, v);
        else modify(u << 1 | 1, x, v);
        pushup(u);
    }
}

④query:查询

void query(int u,int l,int r){
    //如果查询的区间包含某整个区间,就直接返回值
    if(l<=tr[u].l && r>=tr[u].r){
        return tr[u].max;
    }
    //注意mid是tr[u].l+tr[u].r>>1即取树的中点,而不是l+r>>1
    int mid=tr[u].l+tr[u].r>>1;
    int max = Integer.MIN_VALUE;
    //query是左右可以走哪一个就走哪一个
    if (l <= mid) max =Math.max(max, query(u << 1, l, r));
    if (r >= mid+1) max=Math.max(max, query(u << 1 | 1, l, r));
    return max;
}

⑤pushdown:带懒标记的线段树设计,不讲解

posted @ 2022-03-22 11:29  思wu邪  阅读(111)  评论(0编辑  收藏  举报