线段树(Segment Tree)的区间最值查询与更新操作详解
字数 1989 2025-11-23 00:22:00
线段树(Segment Tree)的区间最值查询与更新操作详解
一、问题描述
线段树是一种二叉树结构,用于高效处理数组的区间查询(如区间求和、最值等)和单点/区间更新。本节聚焦于区间最值查询(Range Maximum Query, RMQ) 问题:给定长度为 \(n\) 的数组,需支持以下操作:
- 查询操作
query(l, r):返回区间 \([l, r]\) 内的最大值。 - 更新操作
update(index, value):将位置index的值修改为value。
要求:两种操作的时间复杂度均优于 \(O(n)\),理想目标为 \(O(\log n)\)。
二、线段树的核心思想
-
分治预处理:
- 将数组的每个区间 \([l, r]\) 映射到线段树的一个节点,节点存储该区间的最大值。
- 根节点对应整个数组 \([0, n-1]\),每个非叶子节点 \([l, r]\) 的左右子节点分别对应左半区间 \([l, mid]\) 和右半区间 \([mid+1, r]\)(其中 \(mid = \lfloor (l+r)/2 \rfloor\))。
-
二叉树结构:
- 树高为 \(O(\log n)\),每个操作最多遍历树高路径。
三、线段树的构建
假设数组为 arr = [3, 1, 4, 2, 5],构建过程如下:
-
递归划分区间:
- 根节点:区间 \([0, 4]\),最大值需计算。
- 左子节点:区间 \([0, 2]\),右子节点:区间 \([3, 4]\)。
- 递归直到叶子节点(区间长度为1)。
-
节点存储值:
- 叶子节点:区间内仅一个元素,最大值即为该元素值。
- 非叶子节点:最大值 =
max(左子树最大值, 右子树最大值)。
构建后的线段树结构(括号内为区间范围):
[0,4]: max=5
/ \
[0,2]: max=4 [3,4]: max=5
/ \ / \
[0,1]:4 [2,2]:4 [3,3]:2 [4,4]:5
/ \
[0,0]:3 [1,1]:1
四、区间最值查询操作
查询区间 \([l, r]\) 的最大值,递归规则:
- 当前节点区间完全包含于查询区间:直接返回节点存储的最大值。
- 当前节点区间与查询区间无重叠:返回负无穷(不影响最大值比较)。
- 当前节点区间部分重叠:递归查询左右子树,返回
max(左子树查询结果, 右子树查询结果)。
示例:查询 [1, 3] 的最大值(基于上述树):
- 根节点
[0,4]:部分重叠 → 查询左右子树。- 左子树
[0,2]:部分重叠(查询区间覆盖了其右半部分)→ 继续递归:- 左子节点
[0,1]:完全包含于[1,3]?否(仅部分重叠)→ 递归其左右子树:[0,0]:无重叠 → 返回 -∞。[1,1]:完全包含 → 返回 1。
- 右子节点
[2,2]:完全包含 → 返回 4。 - 合并结果:
max(1, 4) = 4。
- 左子节点
- 右子树
[3,4]:部分重叠 → 递归左子节点[3,3](完全包含,返回 2),右子节点[4,4]无重叠。 - 合并结果:
max(4, 2) = 4。
- 左子树
- 最终结果:4。
时间复杂度:每次递归最多访问左右子树之一,树高为 \(O(\log n)\),故为 \(O(\log n)\)。
五、单点更新操作
更新 index 位置的值,递归规则:
- 定位到叶子节点:递归向下找到对应
index的叶子节点。 - 更新叶子节点值。
- 回溯更新父节点:父节点最大值 =
max(左子树最大值, 右子树最大值)。
示例:将 index=1 的值改为 6:
- 从根节点
[0,4]向下递归:- 左子树
[0,2]→ 其左子节点[0,1]→ 右子节点[1,1](叶子节点)。
- 左子树
- 更新
[1,1]的值为 6。 - 回溯:
[0,1]最大值变为max(3, 6) = 6。[0,2]最大值变为max(6, 4) = 6。- 根节点最大值变为
max(6, 5) = 6。
时间复杂度:更新路径长度 = 树高 \(O(\log n)\)。
六、区间更新与延迟传播(Lazy Propagation)
若需更新整个区间(如区间内所有值加常数),可结合延迟传播优化:
- 延迟标记(Lazy Tag):节点额外存储一个标记,表示“该区间所有值需增加的量”,但尚未递归更新子树。
- 查询/更新时传播标记:当访问子节点前,将标记下推至左右子树,并重置当前标记。
优点:避免每次更新递归到底,将区间更新复杂度也降至 \(O(\log n)\)。
七、总结
- 核心优势:将区间操作转换为 \(O(\log n)\) 的二叉树遍历。
- 适用场景:频繁区间查询、单点/区间更新的静态或动态数组。
- 变体扩展:可通过修改节点存储值(如区间和、区间乘积)适配不同问题。