专栏 | 九章算法
网址 | www.jiuzhang.comjavascript
给出数列[1 4 2 3],求给定区间的最大值
例:query(0,1) = 4 query(2,3) = 3 query(0,3) = 4**java
一道题可不能够用线段树来作,基本是看这道题的操做有没有区间的性质。也就是在一个区间上的操做是否能够转化为两个子区间上的操做。
从这题能够看出:程序员
区间(a,b)的最大值和区间(b,c)的最大值中,取较大的就是区间(a,c)的最大值**复制代码
很明显能够看出这个操做是具备区间的性质。面试
几种常见的线段树的套路算法
1.求区间的和,积,最大值,最match小值,gcd等
2.以当前结点的值做为结点处理,好比给出N个数,再给一个数,问比这个数大的有多少个(固然,用树状数组能够很好的处理,但有修改时,线段树就会方便不少)
3.区间加减同一个值,或者区间同时赋一个值(在面试中,一般不会问到,算法竞赛中会常常用到)复制代码
一颗线段树的构造就是根据区间的性质的来构造的数组
区间划分大概就是上述的区间划分。能够看出每次都将区间的长度一分为二,数列长度为n,因此线段树的高度是log(n),这是不少高效操做的基础。
上述的区间存储的只是区间的左右边界。咱们能够将区间的最大值加入进来。
微信
区间的第三维就是区间的最大值
加这一维的时候只须要在建完了左右区间以后,根据左右区间的最大值来更新当前区间的最大值便可。
由于每次将区间的长度一分为二,全部创造的节点个数为ui
n + 1/2 * n + 1/4 * n + 1/8 * n + ...
= (1 + 1/2 + 1/4 + 1/8 + ...) * n
= 2n复制代码
因此构造线段树的时间复杂度和空间复杂度都为O(n)spa
线段树构造代码模板
3d
构造线段树的目的就是为了更快的查询。
给定一个区间,要求区间中最大的值。
线段树的区间查询操做就是将当前区间分解为较小的子区间,而后由子区间的最大值就能够快速获得须要查询区间的最大值。
query(1,3) = max(query(1,1),query(2,3)) = max(4,3) = 4
上述例子将(1,3)区间分为了(1,1)和(2,3)两个区间,由于(1,1)和(2,3)区间的最大值事先已经记录好了,因此直接拿来用就能够了。
每一层最多查询4个区间
考虑长度为16的序列构形成的线段树,区间(0,15)。查询区间(1,14)。
第一层会查询 (1,7) (8,15)
第二层会查询 (1,3) (4,7) (8,11) (12,14)
第三层会查询 (1,1) (2,3) (12,13) (14,14)
第四层会查询 (1,1) (14,14)复制代码
由于是连续的,中间那部分会直接算出来,而后两边的两端继续往下,仍是4个区间。
由于线段树的高度为log(n),因此查询的时间复杂度为O(log(n))
线段树区间查询模板
线段树构造代码模板
如何把这种变化体现到线段树中去?
例如,将序列中的第4个点更新为5,要变更3个区间中的值,分别为[3,3],[2,3],[0,3]
能够这样想,改动一个节点,与这个节点对应的叶子节点须要变更。由于叶子节点的值的改变可能影响到父亲节点,而后叶子节点的父亲节点也可能须要变更。
因此须要从叶子节点一路走到根节点,去更新线段树上的值。由于线段树的高度为log(n),因此更新序列中一个节点的复杂度为log(n)。
线段树单点更新模板
若是须要区间的最小值或者区间的和。和构造的时候同理。
Segment Tree Build
Segment Tree BuildII
Segment Tree Query
Segment Tree QueryII
Interval Minimum Number
Interval Sum
Interval SumII
Count of Smaller Number
Count of Smaller Number Before itself