线段树【转载】

线段树(Segment Trees)主要用来解决跟区间有关的问题。比如,给你一个数组,要求支持以下操作:

  1. 更新:将某个区间的所有数加上一个数
  2. 查询:查询某个区间的所有数的和

  普通的做法时间复杂度为 O(n) ,每次需要遍历区间内所有的数,但用线段树可以做到 O(log n) 。

  其基本思想就是把区间信息先预处理出来。如果根节点保存 [l, r) 的信息(可以是和,或者最大值、最大值等等),其左右孩子节点则分别保存 [l, (l+r)/2) 和 [(l+r)/2, r) 的信息,然后依次类推。

  查询的时候,只需要选取其中部分节点就可以“拼凑”出要查的区间。所以,线段树只适用于那些总结果可以由子区间的结果合成的问题,比如区间求和、区间最值。

  由于线段树除最后一层外,前面每一层的节点都是满的,因此深度为 O(log n) 。

  关于线段树,黑书上说有这样一个性质:线段树建立好后,其范围内的任何一个区间,都可以用线段树上的不超过 2 log n 条线段表示。

  证明的方法主要是先证每一层最多会被选取两个节点,然后由于高度为 log n 可得。

  为什么每层最多会选取两个呢?试证如下:

  首先,任何一个区间进入线段树,这个区间总会在某个地方被分成两半,除非这个区间的长度为 1 ,那就不用讨论了。分成两半后,前一半由于必须包含 mid 这块区间,它“落下”的时候,每一层最多选取一个节点。后一半同理。


(svg)

  如上图,在线段树中查询线段①,被分成②③两个部分。对于每个部分,可以分两种情况:一种是像②那样,没有达到下一层的一半,此时不会选择 C ;另一种是像③那样,超过下一层的一半,此时会选取 D 。后面的类似。于是一层中最多有两个节点被选取。

  实现方法:一般是像堆那样实现成数组,然后可以用 2 * i 和 2 * i + 1 作为下标分别访问左右孩子。

posted @ 2012-05-23 12:20  wsc500  阅读(49)  评论(0)    收藏  举报