操作一共分为4个基本操作
1.pushup(u) 操作是传入节点编号u,用u节点的子节点信息计算父节点的信息
2.build操作,将一段区间初始化为线段树
3.modify操作,修改某个点或者某个某个区间
某个点
某个区间:使用懒标记,延迟更新
4.查询操作,query()操作
线段树的原理:将一维数组按照递归分段的方式,拆分重构成一颗二叉树,二叉树的每一个节点记录了它的左右儿子节点和少量信息(和你要求解的问题相关)。除了最后一行外,该树是一颗满二叉树
存储类似堆的方式,用一维数组保存线段树
下面来看存储下一颗线段树需要多少内存
因为除了最后一层是满的二叉树,假设倒数第二层有n个点,那么除去最后一层和倒数第二层,有2*n - 1个点;
最后一层至多有2 * n个点
所以线段树开空间时我们至少要开4倍的点,即4
n个点
伪代码如下:
query操作
查找某个区间的最大值
例如查询区间[5, 9]之间的最大值,按照上述的遍历方式,需要遍历的只有橙色标注的几个区间
查询区间的情况一定是小于log(n)次的
[L, R]指的是要查询的区间,TL,TR指的是指的是正在访问的节点的区间
下面证明为啥查询的次数一定小于logn次
如何保证递归的链条最多有2条?或者只是有限条?
只有上述第三类情况中才有可能递归超过一条路径
仔细分析之后发现,这种情况只会出现一次,因为,一旦分裂一次之后,分裂后的区间不会重复出现这种情况,一定会转化为情况1和情况2.
这就保证了,递归路径最多只有2条,故枚举的次数最多只有2log(4*n)次 。
修改线段树
最上边的数组包含了子节点中的所有的数,把它这个整体作为根节点。节点中记录的信息的多少,是满足你要求的,能够通过子节点推导到父节点的最少信息。
节点的个数不会超过4 * n个
所以树的高度是logn
如果修改了单个点的值,要修改更新到整个树上,最多需要logn次
区间修改的话,如果全部点的值都修改了,那么整棵树是4n个点,最多需要修改4n次
复杂度太高了,为了解决提出了pushdown操作
原理类似于query查询操作,只要包含区间,就不用修改该节点的子节点了,给该节点添加懒标记,懒标记就是给以当前节点为根的所有的子节点添加懒标记上标注的值
比如我们要查询的是绿色范围内的总和,对于叶子节点上的值,必须要加上其祖宗节点上的懒标记上的值