简单线段树知识点详解
线段树,顾名思义就是线段成树,这里的线段代表区间,线段树支持的操作是区间统计。
定义什么的特别复杂,我们争取用一张图搞清楚对线段树的直观理解。
上图是一棵1-5区间的线段树。
我们发现这个线段树是一棵二叉树,每个节点表示一个区间,根节点对应区间1-n.每个叶子节点都只表示单点,针对二叉树编号的性质(二叉树的每个父亲节点f的左节点编号是2f,右节点编号是2f+1),我们可以使用一维数组实现线段树。
线段树支持单点查询,区间查询,单点修改,区间修改,我们发现这和树状数组的一些支持项目类似,但是却不完全包含,也就是说,树状数组的所有题目都可以使用线段树统计解决,但是线段树的题目却不一定也能用树状数组解决,换句话说,线段树是树状数组的扩充版本。
它的时间复杂度是O(logn)级别的。
一维数组实现线段树是常见方法。
那么我们回到这个1-5的线段树的图示。
针对于这个图,我们首先要明确每个区间统计的是什么,然后以此为基本建立线段树,也就是说,我们需要用线段树维护一个属性,这个属性是必须要明确的。
我们用例题 校门外的树 数据增强版来更直观地理解。
首先,这道题线段树的每个节点维护的属性就是此节点对应区间中树的数量。
然后我们需要建树
(这是个线段树中很重要的操作)
建树
我们递归建树:
void build(int pos,int l,int r)
{
int mid=l+r>>1;
a[pos]=r-l+1;
if(l==r) return ;
build(lson,l,mid);
build(rson,mid+1,r);
}
我不解释这段代码,其中a为线段树数组,pos为节点编号,l,r为左右端点。
这里还要明确,什么时候建树,什么时候不用建树。
我用一句话概括:
有初值的时候需要建树,没有初值的时候不需要建树(本来就是空的)。
修改
void update(int pos,int l,int r,int x,int y)
{
int mid = l+r>>1;
if(a[pos]==0) return;
if(x<=l && r<=y) {a[pos]=0;return;}
if(y<=mid) update(lson,l,mid,x,y);
else if(x>mid) update(rson,mid+1,r,x,y);
else
{
update(lson,l,mid,x,y);
update(rson,mid+1,r,x,y);
}
a[pos]=a[lson]+a[rson];
}
参数中的x,y表示我们修改的区间。