线段树
线段数是一种二叉树,它主要应对的是多个离线询问或者操作时的回答。线段树可以在 O(logn)的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。。是一种常用的算法结构。也是数据结构中的一种。
线段树的结构非常有规律,我们拿用线段树来存除区间和的样例说明:根节点存储的是整个区间的最值,然后每个节点的左子节点存的是这个节点区间前半部分的和,右节点存储的是这个节点后半区间的值。这样,显而易见可以递归的定义这个函数。
void build(int s, int t, int p) {
if (s == t) {
d[p] = a[s];
return;
}
int m = (s + t) / 2;
build(s, m, p * 2), build(m + 1, t, p * 2 + 1);
d[p] = d[p * 2] + d[(p * 2) + 1];
}
在构造好线段树后可以对线段树的区间进行查询对区间的查询只需要O(logn)。
int getsum(int l, int r, int s, int t, int p) {
if (l <= s && t <= r)
int m = (s + t) / 2, sum = 0;
if (l <= m) sum += getsum(l, r, s, m, p * 2);
if (r > m) sum += getsum(l, r, m + 1, t, p * 2 + 1);
return sum;
}
当然,必不可少的是对区间进行修改和修改过后的查找。这里我蛮直接讲懒惰标记,假设你要对1到N中所有的元素进行修改。而节点刚好代表1到N个元素的和。这时候你可以只修改根节点,然后在根节点进行标记。等到需要向下传递的时候再向下传递。
void update(int l, int r, int c, int s, int t, int p) {
if (l <= s && t <= r) {
d[p] += (t - s + 1) * c, b[p] += c;
return;
}
int m = (s + t) / 2;
if (b[p] && s != t) {
d[p * 2] += b[p] * (m - s + 1), d[p * 2 + 1] += b[p] * (t - m);
b[p * 2] += b[p], b[p * 2 + 1] += b[p];
b[p] = 0;
}
if (l <= m) update(l, r, c, s, m, p * 2);
if (r > m) update(l, r, c, m + 1, t, p * 2 + 1);
d[p] = d[p * 2] + d[p * 2 + 1];
}
用懒惰标记时候的区间查询。
int getsum(int l, int r, int s, int t, int p) {
if (l <= s && t <= r) return d[p];
int m = (s + t) / 2;
if (b[p]) {
d[p * 2] += b[p] * (m - s + 1), d[p * 2 + 1] += b[p] * (t - m),
b[p * 2] += b[p], b[p * 2 + 1] += b[p];
b[p] = 0;
}
int sum = 0;
if (l <= m) sum = getsum(l, r, s, m, p * 2);
if (r > m) sum += getsum(l, r, m + 1, t, p * 2 + 1);
return sum;
}
本文深入讲解了线段树这一高效的数据结构,详细介绍了其构造、查询及更新操作,并通过实例展示了如何利用线段树实现区间求和、区间最大值及最小值等功能。文章还讨论了懒惰标记的应用,以减少不必要的计算。
11万+

被折叠的 条评论
为什么被折叠?



