今天我遇到一道不知道多少棵的线段树求区间第K大的最
值的问题,很好,我线段树我不会用了,现在就让我来看看线段树是一个怎样的东西??
#define INF 0x3F3F3F3F3F3F
const int maxn = 1e5+5;
int input[maxn];//题目输入的数组
int tree[maxn<<2];//用来存最大值
int lazy[maxn]//lazy标记
引入问题:
1.什么是线段树?
线段树是一种二叉搜索树,首先它是一种完全二叉树,每个节点都有
两颗子树,而搜索就是在每个节点代表的区间里进行操作,来实现目
标;
2.线段树的应用范围?
线段树的应用范围很广,包括但不限于维护和查询一段区间上的最
值,区间和。一维线段树可以升级成为二维三维线段树(矩阵树,
空间树);
3.线段树与其他算法的区别
线段树支持在线更新值,而ST算法不支持在线操作。
线段树的基础内容:
要理解线段树首先要了解每个节点存什么,节点的下标是什么,如何建树;
以下我将用一个区间的最大值来说明这个问题:
//递归建树
void build(int k,int l,int r){//k为当前节点,l为要建立区间的左端,
//r为要建立区间的右端
if(l==r){
tree[k]=input[l];
}
else{
int mid = (l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
}
/
现在我们还需要一个更新函数来更新我们需要的最值;
void pushup(int k){
tree[k] = max(tree[k*2],tree[k*2+1]);
}
线段树的基本操作
1.点更新
如何实现点更新,我们先不急看代码,还是对于上面那个线段树,假使
我把a[3]+7,则更新后的线段树应该变成
更新了a[3]之后,每个包含a[3]的节点都需要更新,根据二叉树性质需
要更新log2(n)个节点,我们只要从叶子节点从下而上更新即可
void update(int p ,int v ,int l ,int r,int k){
/*p为目标节点,v为更新值,l为操作区间左端,r为操作区间
右端,k为当前节点
*/
if(l==r){
input[l]+=v;tree[l]+=v;
}else{
int m=(l+r)/2;
if(p<=m){
update(p,v,l,m,k<<1);
}
else{
update(p,v,m+1,r,k<<1|1);
}
pushup(k);
}
}
2.区间查询
区间查询其实和点更新差不多,例如我要查询1~3的区间信息,第一次操作查询[1,6]的区间,发现[1,3]是包含于[1,6]区间,发现[1,3]是包含于[1,6]区间的左儿子,再取操作左儿子节点,儿子节点的范围完美覆盖目标区间。
int query(int L,int R ,int l,int r,int k){
/*
L为目标查询左端,R为目标查询右端,l为当前查询左端,
r为当前查询右端,k为当前操作节点
*/
if(L<=l && R >= r){
return tree[k];
}
else{
int res = -INF;
int mid = (l+r)/2;
if(L <= mid ){
res = max(res , query(L,R,l,mid,k<<1));
}
if(R >= mid){
res = max(res,query(L,R,mid+1,r,k<<1|1));
}
return res;
}
}
- 3.区间更新
我们在这里引入一个新的值lazy标记,该标记用于偷懒,没错就是偷懒
例如我要更新[l,r]区间,是不是要更新其中的每个值,这样的更新的复杂
度就是O(NlogN),程序员们觉得太慢了,所以加入lazy。
我们在更新区间的时候更新到目标区间完全与当前区间符合时就不往
下更新,等下次要要往下查询的时候再去更新,所以lazy标记用来偷
懒.
void pushdown(int k){
if(lazy[k]){
lazy[k<<1]+=lazy[k];
lazt[k<<|1]+=lazy[k];
tree[k<<1]+= lazy[k];
tree[k<<1|1]+=lazy[k];
lazy[k]= 0;
}
}
int update_interval(int L,int R ,int l,int r,int k){
if(L >= l && R <= r){
return tree[k];
}
else{
pushdown(k);
int res = -INF;
int mid = (l+r)>>1;
if(L <= mid ){
res = max(res,update_interval(L,R,l,mid,k<<1));
}
if(R > mid){
res = max(res,update_interval(L,R,mid+1,R,k<<1|1));
}
return res;
}
}
线段树的其他操作
如果你明白了上述线段树处理区间最值的所有操作,那么转变成求最小值以及区间和问题应该也能很快解决,请手动再实现一下查询区间最小值的线段树和查询区间和的线段树。
区间和线段树等代码不再给出,自行实现,若不能实现可以去网上搜索模板对比自己为何不能实现。这里便不再浪费篇幅讲述。
这里我便是想说一下线段树还能处理的问题以及一些具体问题讲解。上述我们只是再讲线段树处理裸区间问题,但是大部分问题不会是让你直接更新查询,而是否真正理解线段树便在于思维是否能从区间跳到线段。
区间只是一个线段的一小部分,还有一些非区间问题也可以演变成一段一段的线段,然后再通过线段树进行各种操作。