众所周知,线段树是OI当中十分重要的一个数据结构,所以我们今天就来讲一讲这个线段树。  

  首先,我们来了解一下什么是线段树。给定一个1~ n的区间,我们考虑,将这个区间进行二分,使得这个区间下拥有两个小区间,如此反复操作,直至这个区间被二分的只剩下一个点,那么这就是这棵线段树的叶节点,线段树的实质上是一颗二叉树,但未必是一颗完全二叉树。

  那么,引入这样的线段树结构有什么用呢?   我们可以发现,对于线段树上的一个结点,其父节点的信息一定是它和它兄弟的综合,也就是说,我们查询一个区间内的信息,就只需要遍历到一个(或多个的综合)代表着该区间的结点,就可以得到我们想要的信息,而不需要遍历线段上的每个点,显然这样的效率是会大大优化的,通过理论证明,我们可以发现,对于一棵线段树,他的每次操作的时间复杂度是O(qlog2(n))的,而它的最大空间复杂度可以近似看为O(4n),事实上可以证明,实际的空间复杂度约为O(大于等于2n的最小的2的整数次方)。这样的空间复杂度是较高的,因此我们常常使用离散化的方式降低线段树的空间复杂度。 

  接下来我们讲一下如何进行编程实现。

  首先,我们考虑如何进行定点修改,显然对于一次修改,我们可以直接通过从整条线段上一直进行二分,找到它所对应的结点,再进行修改,之后回溯修改它父亲结点的信息即可,这样的效率显然是O(log2(n))的。(标程略)

  接下来我们来考虑如何查询区间内的信息,对于一个区间,显然要么它恰好是线段树上的一个区间,要么它就是由多个相邻区间拼凑而成,因此我们同样考虑进行递归回溯,使得这个区间内的所有子区间被查询到且不重复,进行合并处理的出最后答案,事实上这样的效率也是O(log2(n))的。(附简单标程,pushdown会在后文中提到:))

 

1 query(int l,int r,int a,int b,int t){//查询某区间的值 
2 //[l,r]是我们所要查询的区间,[a,b]是当前所递归到的区间
3     if (a==l&&b==r) return tree[t].val;
4     pushdown(t,a,b);
5     int m=(a+b)/2;
6     if (r<=m) return query(l,r,a,m,t*2);
7     if (l>m) return query(l,r,m+1,b,t*2+1);
8     return query(l,m,a,m,t*2)+query(m+1,r,m+1,b,t*2+1);
9 }
点此查看查询函数query

相关文章:

  • 2021-10-22
  • 2021-12-05
  • 2021-11-10
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-01-24
猜你喜欢
  • 2022-12-23
  • 2021-08-06
  • 2021-05-25
  • 2021-10-21
  • 2021-11-26
相关资源
相似解决方案