1.什么是线段树:
线段树简单来说,就是存放整段区间的信息的一种数据结构,它其实是一棵二叉树,而每个节点保存的就是某段区间的信息,比如说区间和,区间内的最值。
2.为什么要使用线段树
在上面的介绍中,我们知道了每个节点保存的是一段区间的信息,但是我们可以通过枚举的方法来得到比如说区间最值,区间和之类的,为什么还要用线段树呢?其实之所以用线段树,是因为你查询的时候有些时候会查询多次,而且有些时候会修改区间的某些值,这样的话多次查询就会很费时间。
而线段树的话依赖树状结构,可以每次查询的时候都降低到log(N),就要比普通操作要节约时间。
3.线段树的构造
定义线段树的数据结构的时候可以选择结构体的方式,或者有些时候将数组开大4倍来保存某个节点的左儿子和右儿子节点的信息
eg.1
接下来以求区间和为例,单点更新
struct TREE{
int data;
int left;
int right;
}tree[maxn<<2];
或者
int sum[maxn<<2];
int max[maxn<<2];
然后建一颗线段树可以通过递归来建立
void push_up(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1+1];
}
void build(int l,int r ,int rt)
{
int mid=(l+r)>>2;
if(r==l)
{
int tmp;
cin>>tmp;
sum[r]=tmp;
return;
}
build(l,mid,rt<<1);
build(mid,r,rt<<1+1);
push_up(rt)
}
建立之后我们改如何更新呢
难道我们直接就在数组上改?
那这样的话我们的线段树上的信息就还是修改之前的,就等于没有修改,那我们该怎样修改呢?
其实仔细想一下,因为我们最终是在树里面查询我们想要的信息,我们每次更新的时候都要把与该节点相关的信息都要变动一下。打个比方,一个公司里某个职位的变动后,就会在那个职位的上级那里去更新信息,然后上级在向上级的上级 继续更新信息。
假设
我们的更新操作是将某个位置的值替换掉
void update(int L,int R,int l,int r,int rt,int change)
{
int m=(l+r)>>1
if(l==r)
{
sum[l]=change;//改成加号就表示把某个位置上加上多少
return ;
}
if(m>L)
update(L,C,l,m,rt<<1,change);
else update(L,C,m,r,rt<<1+1,change);
return ;
}
线段树的查询
理解了线段树的更新原理后,也就理解了线段树的查询原理,两者差不多
int query(int L,int R,int l,int r,int rt)//R,L代表查询区间,r,l,代表的是该节点管辖的区间
{
int ans=0;
if(L==l&&r==R)
{
return sum[rt];
}
int m=(l+r)>>1;
if(R<=m)//
query(L,C,l,m,rt<<1);
else if(L>m)
query(L,C,m,r,rt<<+1);
else
return query(L,C,l,m,rt<<1)+query(L,C,m,r,rt<<1+1);
}
以上就是一个最简单的线段树的例子。
其实可以发现,这个操作和二分有点类似,就是不断的变换节点,如果这个结点在区间里面的话,就返回这个节点的信息,如果区间在m的左边的话,就全部查询左边的,同理,如果在右边的话,就查右子树的,其他情况就把两种的值加起来。
那么如果我要进行的区间操作呢?
如果按照我们之前的操作的话,更新一个点会是logn,那么我们更新n个点就是nlogn,多次询问就会很老火。
那么我们如何解决呢。在这里要介绍一下lazy标记,就是我在更新到一个节点的时候,按照普通的思路应该往下继续更新嘛,这个时候我就不往下更新了,因为我已经把这个节点所管辖的区间的值都已经加到这个sum[rt]了,就没有必要更新子节点了,等到下一次要用子节点的时候再去更新,这样就可以省去很多的麻烦 以及节约时间。
区间修改:某段区间都加c
int lazy[maxn];
void PushDown(int rt,int m) //向下更新,m代表区间的长度
{
if (lazy[rt])
{
lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];
sum[rt<<1] = (m - (m >> 1)) * lazy[rt];
sum[rt<<1|1] = ((m >> 1)) * lazy[rt];
lazy[rt] = 0;
}
}
void update(int L,int R,int c,int l,int r,int rt)
{
if (L <= l && r <= R)
{
lazy[rt] = c;
sum[rt] = c * (r - l + 1);
return ;
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
if (L <= m) update(L , R , c , l,m,rt<<1);
if (R > m) update(L , R , c ,m+1,r,rt<<1+1);
PushUp(rt);
}
int query(int L,int R,int l,int r,int rt)
{
if (L <= l && r <= R)
{
return sum[rt];
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
int ret = 0;
if (L <= m) ret += query(L , R , l,m,rt<<1);
if (m < R) ret += query(L , R , m+1,r,rt<<1+1);
return ret;
}