//这篇博客是不完整的,现在的知识储备,貌似只能写一些基础的应用的
首先的话,来谈下自己对线段树的认识:
线段树可以解决区间加法的问题,比如说,最大值,最小值,区间和的问题,
但是不可以解决不符合区间加法的问题,比如说众数问题。
在网上看了很多版本的线段树,看到了很多的结构体类型的线段树。个人感觉结构体类型的线段树
很好理解,自己觉得的话,线段树主要运用了二分和分治的思想,求一个区间的最大值,
分为两个区间的最大值进行比较,区间和也是先求部分区间的和在求整体。
然后就是运用了二叉树的优点,使得查找时间为logn级别,
修改的话可以分为单点更新和区间更新。区间更新的话需要用到懒惰标记来降低复杂度。
网上其他博客的代码,感觉很好,但是自己敲一遍还是好的。
建树代码:
const int maxn = 100005;
int a[maxn],t[maxn<<2]; //a为原来区间,t为线段树
void Pushup(int k){ //更新函数,这里是实现最大值 ,同理可以变成,最小值,区间和等
t[k] = max(t[k<<1],t[k<<1|1]);
}
//递归方式建树 build(1,1,n);
void build(int k,int l,int r){ //k为当前需要建立的结点,l为当前需要建立区间的左端点,r则为右端点
if(l == r) //左端点等于右端点,即为叶子节点,直接赋值即可
t[k] = a[l];
else{
int m = l + ((r-l)>>1); //m则为中间点,左儿子的结点区间为[l,m],右儿子的结点区间为[m+1,r]
build(k<<1,l,m); //递归构造左儿子结点
build(k<<1|1,m+1,r); //递归构造右儿子结点
Pushup(k); //更新父节点
}
}
单点更新代码:
//递归方式更新 updata(p,v,1,n,1);
void updata(int p,int v,int l,int r,int k){ //p为下标,v为要加上的值,l,r为结点区间,k为结点下标
if(l == r) //左端点等于右端点,即为叶子结点,直接加上v即可
a[l] += v,t[l] += v; //原数组和线段树数组都得到更新
else{
int m = l + ((r-l)>>1); //m则为中间点,左儿子的结点区间为[l,m],右儿子的结点区间为[m+1,r]
if(p <= m) //如果需要更新的结点在左子树区间
updata(p,v,l,m,k<<1);
else //如果需要更新的结点在右子树区间
updata(p,v,m+1,r,k<<1|1);
Pushup(k); //更新父节点的值
}
}
区间查询代码:
//递归方式区间查询 query(L,R,1,n,1);
int query(int L,int R,int l,int r,int k){ //[L,R]即为要查询的区间,l,r为结点区间,k为结点下标
if(L <= l && r <= R) //如果当前结点的区间真包含于要查询的区间内,则返回结点信息且不需要往下递归
return t[k];
else{
int res = -INF; //返回值变量,根据具体线段树查询的什么而自定义
int mid = l + ((r-l)>>1); //m则为中间点,左儿子的结点区间为[l,m],右儿子的结点区间为[m+1,r]
if(L <= m) //如果左子树和需要查询的区间交集非空
res = max(res, query(L,R,l,m,k<<1));
if(R > m) //如果右子树和需要查询的区间交集非空,注意这里不是else if,因为查询区间可能同时和左右区间都有交集
res = max(res, query(L,R,m+1,r,k<<1|1));
return res; //返回当前结点得到的信息
}
}