一 什么是线段树?
线段树是一种二叉树数据结构,用于高效处理区间查询和区间更新问题。它将一个线性区间递归地划分为若干子区间,每个树节点存储相应区间的聚合信息。
1 核心特性
结构:完全二叉树或近似完全二叉树。
存储:每个节点存储一个区间的统计信息。
时间复杂度:
构建:O(n)。
区间查询:O(log n)。
区间更新:O(log n)。
2 基本结构
根节点:[0, n-1]
/ \
0, mid\] \[mid+1, n-1
二 初级线段树算法
1 基础线段树(区间求和)
核心思想:每个节点存储子区间元素的和。
算法步骤:
(1) 构建树:
递归地将区间二分。
叶子节点存储单个元素值。
内部节点存储左右子节点值之和。
(2) 区间查询:
从根节点开始递归。
如果当前区间完全包含在查询区间内,直接返回节点值。
如果与左子区间有重叠,查询左子树。
如果与右子区间有重叠,查询右子树。
合并左右子树结果。
(3)单点更新:
找到对应的叶子节点。
更新叶子节点值?
回溯更新所有祖先节点的和。
2 区间最值查询(RMQ)。
核心思想:每个节点存储子区间的最大值或最小值
算法步骤:
(1)构建过程与求和类似,但节点存储最值而非和。
(2)查询时比较左右子树的最值结果。
(3) 更新时重新计算受影响区间的最值。
三 中级算法
1 懒标记(Lazy Propagation)
核心思想:延迟更新操作,只在需要时执行。
算法步骤:
(1)懒标记设计:
每个节点维护一个懒标记值。
标记表示"该区间需要更新但尚未传递到子节点"。
(2) 区间更新:
更新时如果当前区间完全包含在目标区间内:
更新当前节点值。
设置懒标记(不立即更新子节点)
否则:
先将懒标记下推到子节点。
递归更新左右子树。
更新当前节点值。
(3) 查询时的处理:
遇到有懒标记的节点,先下推标记再查询。
2 区间赋值操作
核心思想:将区间内所有元素设置为指定值
算法步骤:
(1)使用懒标记记录待赋值的值。
(2)下推标记时直接用该值覆盖子节点。
(3)重置子节点的懒标记。
3 区间加值操作
核心思想:给区间内每个元素加上一个固定值
算法步骤:
(1)懒标记记录要加的值。
(2)更新节点值:原值 + 增加值 × 区间长度。
(3)下推时累加子节点的懒标记。
四 高级算法
1 多重懒标记
核心思想:同时支持多种区间操作(如加、乘、赋值)。
算法步骤:
(1)标记优先级:通常赋值 > 乘法 > 加法。
(2)标记处理顺序:
先处理赋值标记。
再处理乘法标记。
最后处理加法标记。
(3) 下推策略:根据标记类型按顺序更新子节点。
2 扫描线算法
核心思想:处理平面矩形面积/周长并集问题。
算法步骤:
(1)事件处理:
将矩形边作为事件(入边/出边)。
按坐标排序处理。
2 线段树维护:
叶子节点表示离散化后的小区间。
节点记录区间被覆盖的次数和覆盖总长度。
动态更新覆盖状态并计算有效长度。
3 主席树(可持久化线段树)
核心思想:保存历史版本,支持查询任意时刻的状态。
算法步骤:
(1)版本管理:
每次更新只创建受影响路径的新副本。
未改变的节点共享引用。
(2) 查询历史:
维护各个版本的根节点。
通过对应版本的根节点访问历史状态。
4 动态开点线段树
核心思想:避免构建完整树结构,按需创建节点。
算法步骤:
(1)初始只有根节点。
(2)在更新过程中动态创建需要的子节点。
(3)适合值域很大但实际数据稀疏的场景。
5 二维线段树
核心思想:扩展到二维平面处理矩形区域查询。
算法步骤:
(1) 树套树结构:
外层线段树管理行区间。
每个外层节点包含一个内层线段树管理列区间。
(2) 操作处理:
在外层树定位行区间。
在内层树处理列区间操作。
时间复杂度:O(log²n)。
五 总结
1 根据问题类型选择
基础统计:区间求和/最值 → 基础线段树。
批量更新:区间修改 → 懒标记线段树。
复杂操作:多种更新类型 → 多重懒标记。
历史查询:版本控制 → 主席树。
稀疏数据:大值域 → 动态开点。
平面问题:二维区域 → 二维线段树/扫描线。
2 性能考虑
(1) 单点操作比区间操作简单。
(2)懒标记增加常数但保持对数复杂度。
(3) 可持久化增加空间复杂度。
(4) 二维线段树时间空间开销都较大。
线段树的核心优势在于其灵活性和效率的平衡,通过不同的节点设计和标记策略,可以适应各种区间处理需求。