快乐树0x02 线段树实现(c++)


一、线段树的基本思想

线段树?

线段树是一种用来维护_区间信息_ 的数据结构。比如需要对一段连续的区间进行修改、查询等大量操作,使用线段树来维护相比线性表能取得更大优势。

线段树的时间复杂度为 O(logN)O(logN)级。

基本思路

线段树的基本思想是将每一个区间长度>1的部分划分成左右两个区间进行递归求解,在一层层划分时将整个线段划分成一个树形结构。这样就可以合并欲求区间包含的子树的信息来求得所求区间包含的全部信息。

这个树形结构是一棵二叉树,因此可以用二叉树的性质,求得:

某区间 pp 的左子区间(左儿子)的节点号为 p2p_2,右子区间(右儿子)节点号为 p2+1p_2+1

Lazy tag

由于线段树要支持对维护数据中的任意一个区间进行修改,而线段树是一个树形结构,如果每次修改都要对涉及的所有节点都去修改,其修改效率反而不如线性表。实际上,对欲修改区间,如果线段树某节点的区间正好全部在欲修改区间中,那么只需要修改该节点的值就可以了,不需要再深入到子节点中进行修改,因为至少目前用不到。对该节点进行修改后,我们给该节点打上一个标记,表明我们这次修改的数量,如果以后需要获取其子节点中的值,我们再把之前这次修改进行落实。这样,如果以后不需要其子节点的值,我们就省下了修改其子节点 的时间。

这个lazy tag的方法有点像初学的时候做的一道铺地毯的题,相比去修改每个被地毯覆盖的点,记录每次铺地毯的范围并与欲求坐标进行比对效率更高。

二、代码实现

建立线段树

递归分割建树。树的节点总数大致是原数集大小的4倍,开4*maxn。

getsum函数

写一个函数,取得区间和。依然是递归,递归边界是当前区间全部在所求区间内,就返回当前节点值。

对于遇见的lazy tag,将tag记录的修改作用到子节点上,然后把标记下放到子节点中。注意,所有被标记的节点是已经被作用了修改的,而其子节点还没有被作用修改。

add函数

用于区间修改(加或减)。使用lazy tag:在发现当前节点完全包含在目标节点时,就没有必要再修改子节点了,直接给本节点的值修改 修改值×本节点表示的长度修改值×本节点表示的长度 即可,同时在本节点打下标记。同样,在遇到标记的时候,要先更新子节点并下沉标记到子节点,然后再拆分递归。当然,没有必要处理叶子节点的标记,因为他们没有子节点。

调用示例

注意,建树时节点编号必须>0,否则n*0都是0,整个树都会发生错误。


提供区间加减法、区间求和的完整代码模板:


提供区间加减、乘法、区间求和的代码模板:

最后更新于