原博地址https://laboo.top/2018/11/24/xds/#more算法
线段树算法是一种快速查询一段区间内的信息的算法, 因为其实现简单, 因此普遍应用于程序设计竞赛中。
线段树是一棵完美二叉树, 即全部的叶子节点的深度均相同, 而且全部的非叶子节点都有两个子节点。每一个节点维护一个区间, 这个区间为父节点二分后的子区间, 根节点维护整个区间, 叶子节点维护单个元素, 当元素个数为n
时, 对区间的操做均可以在O(log n)
的时间内完成, 由于此时树的深度为log2 n + 1
, 每次操做只需从叶子节点开始, 往上更新至根节点, 每层只需更新相关的一个区间便可, 操做次数log2 n + 1
, 即在O(log n)
的时间内可完成。数组
线段树能够提供不一样的功能, 例如最多见的求区间内的最大最小值和求区间内的和, 还有其余相似的功能, 实现思路基本相同spa
给定任意数列[a0, a1,...,an-1]
, 在O(log n)
的时间内完成下列的两种操做设计
query(s, t)
求 [as,as+1,...,at-1]
内的最小值(最小值)update(i, x)
把 ai
的值改成 x
给定初始值全为0
的数列[a0, a1,...,an-1]
, 在O(log n)
的时间内完成下列的两种操做code
query(s, t)
求 [as,as+1,...,at-1]
内的和add(i, x)
执行 ai += x
这里咱们以求区间最小值
内的最小值为例, 用Python
来实现原始的一棵线段树递归
这里建立一个数组dat[]
并赋予初始最大值, 为了让其成为一棵完美的二叉树, 便于计算, 咱们把n
扩大到2的幂
, 因为咱们在数组中填充了int32
的最大整数2147483647
, 因此多余出来的的元素老是最大值, 不会影响原来区间的结果图片
def init(self, n): self.INT_MAX = 2147483647 self.n = 1 while self.n < n: self.n *= 2 self.dat = [self.INT_MAX for i in range(2 * self.n - 1)]
咱们把一棵完美二叉树压成一个数组, 下标为i
的子节点为i*2+1 和 i*2+2
, a0
为根节点, 每次更新时, 首先更新叶子节点, 以后一层层往上更新, 节点a[k] = min(a[k * 2 + 1],a[k * 2 + 2])
, 操做在O(log n)
的时间内完成get
def update(self, k, a): k += self.n - 1 self.dat[k] = a while k > 0: k = (k - 1) // 2 self.dat[k] = min(self.dat[k * 2 + 1],self.dat[k * 2 + 2])
query
的功能为查询[a, b)
区间内的最小值, 参数k, l, r
是辅助参数博客
当[a,b)
, 不在k
节点管理的区间[l, r)
内时, 直接返回INT_MAX
当[a,b)
, 重合于k
节点管理的区间[l, r)
时, 直接返回k
节点的值
不然, 递归k
的两个子节点, 返回其中的最小值it
def query(self, a, b, k, l, r): if r <= a or b <= l: return self.INT_MAX if a <= l and r <= b: return self.dat[k] else: vl = self.query(a, b, k * 2 + 1, l, (l + r) // 2) vr = self.query(a, b, k * 2 + 2, (l + r) // 2, r) return min(vl, vr)
至此咱们就简单地实现了一棵线段树, 这只是线段树的其中一种形式, 线段树还有其余的变体。线段树的使用实例能够看个人另外一篇文章https://laboo.top/2018/11/02/acm-lc-45/#more
欢迎关注个人博客公众号
![]()