【笔记】线段树

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cbwem/article/details/79474019

  与区间有关的操作,比如统计线段的长度、记录一个区间内子线段的分布、统计落在区间内的数据频率等,并在线段或数据的插入、删除和修改中维护这些特征值。线段树具有良好的树形二分结构,能够高效地完成这些操作。

一、线段树的基本概念

  一颗二叉树,即为T(a,b),参数a,b表示该节点表示区间[a,b]。区间长度b-a记为L。递归定义T[a,b]:
  若区间长度L>1:区间 [ a , a + b 2 ] 为T的左儿子,区间 [ a + b 2 + 1 , b ] 为T的右儿子。
  若区间长度L=1:T为一个叶子结点,表示区间[a]。


这里写图片描述

  叶结点为区间内的所有数据点。在对分枝结点的区间中点分界时,数据点要么落在左子树的底层,要么落在右子树的底层。如果进行区间运算的话,则可能存在“跨越”的情况,删除或插入区间内的每个点时都必须深入到叶结点,因此一般来说需要 l o g 2 n 的时间复杂度。线段树一方面可以方便计数,另一方面由于它实际上是排序二叉树,所以容易找出最大值和最小值来。
  线段树一般采用结构数组a[]存储,如果结点a[i]代表区间[l,r]的话,则左儿子a[2*i]代表左子区间 [ l , l + r 2 ] ,右儿子a[2*i+1]代表右子区间 [ l + r 2 + 1 , r ] 。每个节点除需要记录所代表区间的左右指针外,还可根据需要增设一些特殊的数据域,例如所代表的子区间是否为空;如果不为空的话,有多少线段覆盖本子区间,或哪些数据落在本子区间内,以便插入或删除线段时动态维护。

二、线段树的基本操作

  线段树的基本操作包括:

  • 建立线段树
  • 在区间内插入线段或数据
  • 删除区间内的线段或数据
  • 动态维护线段树

1.建立线段树

  在区间[l,r]插入或删除线段操作前,需要为该区间建立一颗线段树,依照二分策略将区间[l,r]划分出 t o t ( t o t 2 l o g 2 ( r l ) ) 个空的子区间,这些子区间暂且未被任何线段所覆盖。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过程,设置结点序号、左右指针和数据域初始化。

猜你喜欢

转载自blog.csdn.net/cbwem/article/details/79474019