初步学习线段树

                                                前几天学了树链剖分,调了好久结果发现是线段树写挂了,于是决定写个blog总结一下。————作者

    假如给你一组数,要求你做若干个操作,操作有两种: 1、把一个区间的数加上k。 2、查询某个区间的区间和

  显然我们可以用O(N)的时间复杂度完成这两个操作。

  但假如操作个数和N的规模非常大,比如达到了10^5的规模,那么朴素做法就太慢了。因此,我们需要一个新的东西——线段树。

  什么是线段树(Segment Tree)?

  线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。(摘自百度百科)。

  简单的说就是在树上的每一个节点存储一个区间上的信息,一个点的左右儿子分别储存该点所存的左半个区间和右半个区间。

  举个栗子,假如给我们一组数1,2,6,3,8,9,5,7,4,5,我们想储存每个区间的区间和。那么这个线段树大概长这样:

              

                              (每个节点存储节点所代表区间的区间和)

  线段树可以做什么?

  线段数支持:1 单点/区间修改(update)

           2 单点/区间查询 (query)

    它们的时间复杂度都是O(log n)的

  做法(思路)

  建树(build):我们要每次二分一个区间,然后分别存入左右儿子中,这个过程是相似的,因此我们可以用递归完成建树。当区间长度为1时,我们把数列中数的值存入叶子结点,然后向上传递。

  

     

  更新(update):如果我们要更新某一段区间A的值,那么显然我们不仅要更新A所在的节点,同时要更新A所包含的区间,和包含A的区间。

  我们依然递归完成这个任务,但update和build有一些区别,我们来思考。(下面的假设请对照上图)

  假如我们要修改[2,2] , 那么我们直接简单的递归到[2,2]然后向上更新父节点的值即可。

  假如我们要更新[4,6],那么问题来了,这个区间处在两个子树中,我们无法找到直接代表这个区间的节点。但这样我们就没办法修改了吗?不是的。我们可以把[4,6]拆成[4,5]和[6,6]分别修改。这只需要在向下递归时加一个判断。

  查询(query):查询的步骤和更新是相似的。

    干巴巴的说不知道大家能不能看懂。我们来看一下代码实现。

  用结构体储存

1 struct Tree{
2     int l,r,sum;
3 }tree[N*4];//N是数列长度,我们通过图可以很清楚的发现节点个数是数列长度的4倍多一点

  build函数的实现

待更新~

猜你喜欢

转载自www.cnblogs.com/FoxC/p/11222068.html