线段树

来源:互联网 发布:solidworks软件下载64 编辑:程序博客网 时间:2024/06/05 11:09

  线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
  使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。

定义:
  线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
  对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
  使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。

基本结构:
   线段树是建立在线段的基础上,每个结点都代表了一条线段[a,b]。长度为1的线段称为元线段。非元线段都有两个子结点,左结点代表的线段为[a,(a + b) / 2],右结点代表的线段为[((a + b) / 2)+1,b]。
  下图是长度范围为[1,10]的线段树。
  这里写图片描述
  长度范围为[1,L] 的一棵线段树的深度为log (L) + 1。这个显然,而且存储一棵线段树的空间复杂度为O(L)。
  线段树支持最基本的操作为插入和删除一条线段。下面以插入为例,详细叙述,删除类似。
  将一条线段[a,b] 插入到代表线段[l,r]的结点p中,如果p不是原线段,那么令mid=(l+r)/2。如果b<mid,那么将线段[a,b] 也插入到p的左儿子结点中,如果a>mid,那么将线段[a,b] 也插入到p的右儿子结点中。插入(删除)操作的时间复杂度为O(logn)。


理解:
  线段树上的每一个节点T[a , b],代表该节点维护了原数列[ a , b ]区间的信息。
  对于每一个节点他至少有三个信息:左端点,右端点,我们需要维护的信息(在本题中我们维护区间和)。
  由于线段树是一个二叉树,而且是一个平衡二叉树,如果当前结点的编号是i,左端点为L ,右端点为 R , 那么左儿子的 编号为 i*2 ,左端点为 L ,右端点为 (L + R)/2 ; 同理右儿子的 编号为 i*2+1,左端点为(L+R)/2 ,右端点为 R。 
  如果当前结点的左端点等于右端点,那么该节点就是叶子节点,直接在该节点赋值即可。显然线段树是递归定义的。
  线段树就是这样一种数据结构,讲一个大区间分为若干个不相交的区间,每次维护都在小区间上处理,并且查询也在这些被分解的区间中信息合并出我们需要的结果,这就是线段树高效的原因。
  


建树:
  线段树的构建是自顶点而下,即从根节点开始递归构建,根据线段树定义,当左端点等于右端点时(达到递归边界),直接赋值即可,回溯时也要维护区间,代码如下:

void Build_Tree ( int x , int y , int i ){    tr[i].l = x;    tr[i].r = y;    if( x == y )tr[i].sum = a[x] ; //找到叶子节点,赋值    else    {        ll mid = (tr[i].l tr[i].r ) >> 1 ;        Build_Tree ( x , mid , i << 1); //左子树        Build_Tree ( mid + 1 , y , i << 1 | 1); //右子树        tr[i].sum = tr[i << 1].sum + tr[i << 1 | 1].sum; //回溯维护区间和    }}

维护树:
  维护树的方法也很好理解,如果目标更新节点在左儿子里,去左儿子中查找;反之,在右儿子中。不断递归,知道找到需要维护的节点,更新它,回溯是一路更新回来。这就是维护的过程,代码如下:

void Update_Tree ( int q , int val , int i ){    if(tr[i].l == q && tr[i].r == q) //找到需要修改的叶子节点    {        tr[i].sum = val ; //更新当前结点    }    else //当前结点是非叶子结点    {        long long mid = (tr[i].l tr[i].r ) >> 1 ; //取中间        if ( q <= mid ) //目标节点在左儿子中        {            Update_Tree ( q , val , i << 1 );        }        else if( q > mid ) //目标节点在右儿子中        {            Update_Tree ( q , val , i << 1 | 1 );        }        tr[i].sum = tr[i << 1].sum + tr[i << 1 | 1].sum; //回溯    }}

查询树:
  查询区间求和,不难想到如果当前结点的区间完全被目标区间包含,直接返回当前结点的sum值,否则分类讨论。具体过程通过以下代码理解:
  

long long Query_Tree ( int q , int w , int i ){    if ( q <= tr[i].l && w >= tr[i].r ) return tr[i].sum; //当前结点的区间完全被目标区间包含    else    {        long long mid = (tr[i].l tr[i].r) >> 1;        if( q > mid ) //完全在左儿子        {            return Query_Tree ( q , w , i << 1 | 1);        }        else if (w <= mid ) //完全在右儿子        {            return Query_Tree ( q , w , i << 1);        }        else //目标区间在左右都有分布        {            return Query_Tree ( q , w , i << 1) + Query_Tree ( q , w , i << 1 | 1 );        }    }}
阅读全文
'); })();
1 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 正畸蜡怎么用 正畸蜡使用方法 正畸保护蜡 正畸保护蜡怎么用 正畸蜡 正镶白旗招标网 正直玩家 勇敢的号角 正相关 正相关和负相关 正省级城市 比亚迪f3汽油滤芯正确安装图 几率和机率哪个正确 正确率怎么算 公务员行测正确率 012路规律正确率 正磷酸铁 正磷酸钾 永福寺 永福寺门票 福州种福寺 杭州永福寺住宿 碳正离子稳定性 碳正离子 碳正离子的稳定性顺序 正科级 正科级工资大概多少 正科级工资 正科级办公室面积标准 2018正科级一个月工资多少 新邵正科级干部名单 2018年正科级工资多少一个月 科级干部 科级 立可明眼镜 山东立明科技职业学院 正的笔顺 正笔顺怎么写 正的笔顺笔画顺序表 正字的笔顺正确的写法 正的笔顺笔画顺序 正的正确笔顺