Java教程

训练日志2:线段树简述

本文主要是介绍训练日志2:线段树简述,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

线段树概念:

线段树是一种二叉搜索树,其存储的是一个区间的信息,用[L,R]表示从L到R之间存在的信息。

在这里插入图片描述

在这里插入图片描述

线段树的延迟更新:

在结构体中再定义一个对象 lazy,add记录的是此节点所代表的区间所有数被加的值,在我们进行操作时,比如加减,我们就可以利用lazy然后往下修改,而不用修改一次就遍历一次,大大节约时间。
在这里插入图片描述

线段树的基本操作:

建树、单点查询、单点修改、区间查询、区间修改

结点的构建:

const int maxn=1e6;
struct node{
int l, r,lazy,val;
}tree[maxn<<2];//线段树要开数组的4倍

以求和为例

建树:

void build(int l,int r,int rt){
    tree[rt].lazy=0;//初始化lazy标记
    tree[rt].l=l;
    tree[rt].r=r;
    if(l==r){
        tree[rt].val=a[l];
        return;
    }
    else{
        int mid=(l+r)>>1;
        build(l,mid,rt*2);//左孩子
        build(mid,r,rt*2+1);//右孩子
        tree[rt].val=tree[rt*2].val+tree[rt*2+1].val;状态合并,此结点的为两个孩子的val和
    }
}

单点查询

int querynode(int rt){//单点查询(二分)
    if(tree[rt].l==tree[rt].r){//当前结点的左右端点相等,为叶子节点,是最终答案
        return tree[rt].val;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(rt<=mid)//目标位置比中点靠左,就递归左孩子
        querynode(rt*2);
    else//反之,递归右孩子
        querynode(rt*2+1);
}

单点修改

void updatenode(int rt,int c){
    if(tree[rt].l==tree[rt].r){//找到目标位置
        tree[rt].val+=c;
        return;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(rt<=mid)//目标位置比中点靠左,就递归左孩子
        updatenode(rt*2,c);
    else//反之,递归右孩子
        updatenode(rt*2+1,c);
    tree[rt].val=tree[rt*2].val+tree[rt*2+1].val;//所有包含结点k的结点状态更新
}

区间查询:

int query(int l,int r,int rt){//区间查询
    if(tree[rt].l>=l&&tree[rt].r<=r)
        return tree[rt].val;
    else if(l>l||r<1)
        return 0;
    else{
        return query(l,r,rt<<1)+query(l,r,rt<<1|1);
    }
}

区间修改:

为了避免超时,引用延迟标记
每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新 or 询问的时候。下次更新或者查询的时候,如果查到该节点,就把延迟标记进行下传,将值加到他的子节点上去,同时将延迟标记变为 0,避免下次重复更新。这样只更新到查询的子区间,不需要再往下找了,极大的降低了时间复杂度。
在这里插入图片描述
下推:

void pushdown(int rt){//下推
    tree[rt*2].lazy+=tree[rt].lazy;//左孩子更新延迟标记
    tree[rt*2+1].lazy+=tree[rt].lazy;//右孩子更新延迟标记
    tree[rt*2].val+=tree[rt].lazy*(tree[rt*2].r-tree[rt*2].l+1);//左孩子状态更新
    tree[rt*2+1].val+=tree[rt].lazy*(tree[rt*2+1].r-tree[rt*2+1].l+1);//右孩子状态更新
    tree[rt].lazy=0;//清除本结点标记
}

区间修改:

void updata(int l,int r,int rt,int c){//区间更新
    if(tree[rt].l>=l&&tree[rt].r<=r){
        tree[rt].val+=c*(tree[rt].r-tree[rt].l+1);
        tree[rt].lazy+=c;
        return;
    }
    pushdown(rt);
    updata(l,r,rt*2,c);
    updata(l,r,rt*2+1,c);
    tree[rt].val=tree[rt*2].val+tree[rt*2+1].val;

}

明天整理题目

这篇关于训练日志2:线段树简述的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!