线段树的概念: 线段树是算法竞赛中常用的用来维护 区间信息 的数据结构。
线段树可以在 O ( l o g N ) O(logN) O(logN) 的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。
线段树的结构(文字解释):
线段树的结构(下图所示):
对于线段树最少要开4倍空间的解释:
线段树的节点关系:
x
x / 2
等效于x >> 1
x * 2
等效于x << 1
x * 2 + 1
等效于x << 1 | 1
在介绍 b u i l d ( ) build() build() 函数之前先介绍 p u s h u p ( ) pushup() pushup() 函数;
p u s h u p ( ) pushup() pushup()函数含义为:由子节点信息更新父节点信息
例子:单点修改(求最大值,最小值等 …)
**俗称:**懒标记(支持区间修改)
以区间加为例:
含义: 给当前区间的所有儿子加上 add(懒标记)
如图:
建立线段树:
一般模板为:
函数参数为:当前区间的编号u 当前区间的左端点l 当前区间的右端点r
void build(int u, int l, int r)
{
tr[u] = {l, r};
if(l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
以查询区间最大值为例:
其中
[
L
,
R
]
[L, R]
[L,R] 为 要查询的区间,
[
T
L
,
T
R
]
[T_L, T_R]
[TL,TR] 为当前区间的范围
则有如下几种情况:
具体操作:
1.
[
L
,
R
]
⊃
[
T
L
,
T
R
]
[L, R] \supset [T_L, T_R]
[L,R]⊃[TL,TR]
如图:
即、树中区间在我们要查询区间的内部:直接返回最大值(树中保存的信息即可)
2.
[
L
,
R
]
∩
[
T
L
,
T
R
]
≠
∅
[L, R] \cap [T_L, T_R] \neq \varnothing
[L,R]∩[TL,TR]=∅
如图:
包含如下几种情况:
总结
查询区间于树中区间有交集,则递归交集的部分:左边有交集递归左边,右边有交集递归右边
一般只有两条链递归至叶子节点,时间复杂度为 O ( l o g N ) O(logN) O(logN),但 l o g N logN logN 的常数较大(比树状数组,st表等都大)
3.
[
L
,
R
]
∩
[
T
L
,
T
R
]
=
∅
[L, R] \cap [T_L, T_R]= \varnothing
[L,R]∩[TL,TR]=∅
不存在此种情况!!!