线段树(一)
线段树是一种维护区间信息常用的树形数据结构。在全国青少年信息学奥林匹克竞赛大纲内难度评级为 6,是提高级中开始学习的数据结构。
本篇文章讨论的内容是线段树的基本结构与操作、线段树的延迟更新。
代码模板
基本结构
线段树是用来维护区间信息的树形结构,每个节点表示一个区间的信息。
通常使用存储完全二叉树的数组存储法来存线段树,具体地,节点 $p$ 的左右子树分别是 $p2$ 和 $p2+1$ 节点。
理论上,线段树最多只有 $2N-1$ 个节点,但是在某些情况下,下标会超过 $2N$ (例如 $N=6$ 时的线段树节点下标最大到了 $13$),所以线段树一般开 $4$ 倍空间。
每个节点存储一个区间的信息,其中,根节点存储整个序列 $[1,N]$ 的信息,设节点 $p$ 存储区间 $[L,R]$ 的信息,则节点 $p2$ 和 $p2+1$ 分别存储区间 $[L,mid]$ 和 $[mid+1,R]$ 的信息,其中 $mid=\lfloor \frac{L+R}{2} \rfloor$。区间大小为 $1$ 的节点是线段树的叶子节点。
下图说明了线段树的结构,图来自《算法竞赛进阶指南》:
基本操作:建立,单点修改与查询
线段树的建树操作递归实现,对于一个节点的建立,先建立他的左右子树,再根据他的左右子树信息合并得到他的信息(从下往上传递信息)。
线段树的单点修改,要先递归找到修改的叶子节点,然后将修改后的信息向上传递。单点查询即找到这个点代表的叶子节点,代码实现与单点修改类似。
线段树的区间查询,通过判断查询的区间是否和左右子树表示的区间有交集,合并有交集的区间内的信息。
时间复杂度分析:建树操作为 $n \log n$,每次单点修改或查询需要经过树上的一条链,时间复杂度为 $O(\log n)$。每次区间查询操作会把询问在树上的 $\log n$ 个节点。
参考代码(维护的信息是区间最大值):
洛谷 P1198 [JSOI2008] 最大数
1 | // https://www.luogu.com.cn/problem/P1198 |
延迟更新:区间修改
线段树最强大的功能是可以 $O(\log n)$ 实现的区间修改,显然不是对区间内每个点进行单点修改。
具体的,如果要修改一个区间,那就按照类似区间查询的实现方式,从根节点开始递归。如果当前节点不完全包含修改区间就向左右子树递归,否则就在这个节点上记录一个延迟更新(lazy tag)数据,返回即可。
lazy tag 是含义是:该节点被修改,但是修改数据没有下传到其子节点。再次修改或查询某节点时,将这个节点上的 lazy tag 下传到其子节点。时间复杂度与区间查询相同。
代码实现如下:
洛谷 P3372 【模板】线段树 1
1 |
|
推荐题目 && 参考资料 && 拓展阅读
- 《算法竞赛进阶指南》 0x43 线段树
- P3870 [TJOI2009] 开关
- P1438 无聊的数列
- P1253 扶苏的问题
- P3373 【模板】线段树 2
- P4513 小白逛公园
- P1471 方差