与区间有关的操作,比如统计线段的长度、记录一个区间内子线段的分布、统计落在区间内的数据频率等,并在线段或数据的插入、删除和修改中维护这些特征值。线段树具有良好的树形二分结构,能够高效地完成这些操作。
一、线段树的基本概念
一颗二叉树,即为T(a,b),参数a,b表示该节点表示区间[a,b]。区间长度b-a记为L。递归定义T[a,b]:
若区间长度L>1:区间
为T的左儿子,区间
为T的右儿子。
若区间长度L=1:T为一个叶子结点,表示区间[a]。
叶结点为区间内的所有数据点。在对分枝结点的区间中点分界时,数据点要么落在左子树的底层,要么落在右子树的底层。如果进行区间运算的话,则可能存在“跨越”的情况,删除或插入区间内的每个点时都必须深入到叶结点,因此一般来说需要
的时间复杂度。线段树一方面可以方便计数,另一方面由于它实际上是排序二叉树,所以容易找出最大值和最小值来。
线段树一般采用结构数组a[]存储,如果结点a[i]代表区间[l,r]的话,则左儿子a[2*i]代表左子区间
,右儿子a[2*i+1]代表右子区间
。每个节点除需要记录所代表区间的左右指针外,还可根据需要增设一些特殊的数据域,例如所代表的子区间是否为空;如果不为空的话,有多少线段覆盖本子区间,或哪些数据落在本子区间内,以便插入或删除线段时动态维护。
二、线段树的基本操作
线段树的基本操作包括:
- 建立线段树
- 在区间内插入线段或数据
- 删除区间内的线段或数据
- 动态维护线段树
1.建立线段树
在区间[l,r]插入或删除线段操作前,需要为该区间建立一颗线段树,依照二分策略将区间[l,r]划分出 个空的子区间,这些子区间暂且未被任何线段所覆盖。tot为全局变量,记录一共用到了多少个节点。建树前tot=0,建立线段树T(l,r)的过程:
void build_tree(int l,int r,int i)
{
节点i的数据域初始化
if(1==r)
{
设置数据所在的节点序号
}
int mid=(l+r)/2;
build_tree(1,mid,i+i);
build_tree(mid+1,r,i+i+1);
}
在插入、删除线段或数据操作前,一般需要调用Build过程,设置结点序号、左右指针和数据域初始化。