上面讲的是对线段树的点更新和区间查询,若要求对区间中的所有点都进行更新,该怎么办?若对区间的每个点都进行更新,则时间复杂度较高,可以引入懒操作。下面讲解带有懒标记的区间更新和区间查询操作。
对[ l , r ]区间进行更新,例如将[ l , r ]区间的所有元素都更新为 v ,步骤如下。
(1)若当前节点的区间被查询区间[ l , r ]覆盖,则仅对该节点进行更新并做懒标记,表示该节点已被更新,对该节点的子节点暂不更新。
(2)判断是在左子树中查询还是在右子树中查询。在查询过程中,若当前节点带有懒标记,则将懒标记下传给子节点(将当前节点的懒标记清除,将子节点更新并做懒标记),继续查询。
(3)在返回时更新最值。
例如,在[1, 10]区间的线段树中修改[4, 8]区间的值为20,其过程如下。
(1)先判断[4, 8]区间是否覆盖树根区间[1, 10],树根划分点mid=(1+10)/2=5,查询区间横跨树根的左右子树区间,分别到左右子树中查找区间[4, 8],若当前节点有懒标记,则下传懒标记。
(2)在左子树[1, 5]中查找更新区间[4, 8],划分点mid=(1+5)/2=3,查询区间在右子树中,到右子树[4, 5]中查找区间[4, 8],若该节点有懒标记,则下传懒标记。更新区间[4, 8]正好覆盖[4, 5]区间,更新最值并做懒标记。
(3)在右子树[6, 10]中查找更新区间[4, 8],划分点mid=(6+10)/2=8,查询区间在左子树中,到左子树[6, 8]中查找更新区间[4, 8],若该节点有懒标记,则下传懒标记。更新区间[4, 8]正好覆盖[6, 8]区间,更新最值并做懒标记。
(4)返回时,更新节点的最值为其左右子树最值的最大值。
算法代码:
带有懒标记的区间查询和普通的区间查询有所不同,在查询过程中若遇到节点有懒标记,则下传懒标记,继续查询。例如,查询[6, 7]区间的最值,过程如下。
(1)求树根[1, 10]的划分点,mid=(1+10)/2=5,查询区间[6, 7]在右子树中,继续判断,经过[6, 8]区间时,该节点有懒标记,则下传懒标记(当前节点的懒标记清除,下传至左右子节点)。
(2)继续判断,找到[6, 7]区间,返回该区间的最值即可。
算法代码:
线段树采用了分治算法策略,其点更新、区间更新、区间查询均可在 O (log n )时间内完成。树状数组和线段树都用于解决频繁修改和查询的问题,树状数组可以实现点更新、区间和查询,线段树可以实现点更新、区间更新和区间查询。树状数组比线段树节省空间,代码简单易懂,但是线段树用途更广、更灵活,凡是可以用树状数组解决的问题都可以用线段树解决。