📅  最后修改于: 2023-12-03 15:07:09.538000             🧑  作者: Mango
分段树(Segment Tree)是一种常见的数据结构,用于解决区间查询问题,例如区间最值、区间和等等。而在分段树中,为了提高查询的效率,我们通常需要进行一些预处理,其中一个常见的预处理方法就是延迟传播。
在分段树中,为了避免重复计算,我们通常使用线段树的思想,将区间一分为二,然后将左右两个子区间的计算结果合并得到父区间的计算结果。但是,在进行区间修改时,可能会导致子区间的计算结果失效,因此我们需要对修改操作进行优化,而延迟传播就是一种优化方法。
延迟传播的核心思想是,当进行区间修改操作时,不直接修改子区间的值,而是标记该区间需要进行修改,并将修改操作延迟到该区间被查询时再进行。
在实现延迟传播时,我们需要在节点中添加一个标记(通常称为lazyTag),用来表示该节点需要进行的修改操作。在每次进行区间合并操作时,我们需要将左右子节点的lazyTag进行合并,然后根据lazyTag对节点进行更新。在查询节点的值时,如果该节点的lazyTag不为空,则需要将lazyTag更新到子节点,并将lazyTag重置为null。
/**
* 包含 LazyTag 的线段树节点类
*/
class SegmentTreeNode {
int start;
int end;
int sum;
int lazyTag;
public SegmentTreeNode(int start, int end) {
this.start = start;
this.end = end;
}
}
/**
* 分段树类,包含延迟传播操作
*/
class SegmentTree {
private SegmentTreeNode[] tree;
public SegmentTree(int[] nums) {
tree = new SegmentTreeNode[nums.length * 4];
buildTree(0, 0, nums.length - 1, nums);
}
private void buildTree(int index, int start, int end, int[] nums) {
tree[index] = new SegmentTreeNode(start, end);
if (start == end) {
tree[index].sum = nums[start];
return;
}
int mid = start + (end - start) / 2;
buildTree(index * 2 + 1, start, mid, nums);
buildTree(index * 2 + 2, mid + 1, end, nums);
tree[index].sum = tree[index * 2 + 1].sum + tree[index * 2 + 2].sum;
}
public void updateRange(int start, int end, int val) {
updateRangeHelper(0, start, end, val);
}
private void updateRangeHelper(int index, int start, int end, int val) {
if (tree[index].start >= start && tree[index].end <= end) {
tree[index].lazyTag += val;
tree[index].sum += (tree[index].end - tree[index].start + 1) * val;
return;
}
pushDown(index);
int mid = tree[index].start + (tree[index].end - tree[index].start) / 2;
if (start <= mid) {
updateRangeHelper(index * 2 + 1, start, end, val);
}
if (end > mid) {
updateRangeHelper(index * 2 + 2, start, end, val);
}
tree[index].sum = tree[index * 2 + 1].sum + tree[index * 2 + 2].sum;
}
public int queryRange(int start, int end) {
return queryRangeHelper(0, start, end);
}
private int queryRangeHelper(int index, int start, int end) {
if (tree[index].start >= start && tree[index].end <= end) {
return tree[index].sum;
}
pushDown(index);
int mid = tree[index].start + (tree[index].end - tree[index].start) / 2;
int sum = 0;
if (start <= mid) {
sum += queryRangeHelper(index * 2 + 1, start, end);
}
if (end > mid) {
sum += queryRangeHelper(index * 2 + 2, start, end);
}
return sum;
}
private void pushDown(int index) {
if (tree[index].lazyTag != 0) {
int leftIndex = index * 2 + 1;
int rightIndex = index * 2 + 2;
tree[leftIndex].lazyTag += tree[index].lazyTag;
tree[leftIndex].sum += (tree[leftIndex].end - tree[leftIndex].start + 1) * tree[index].lazyTag;
tree[rightIndex].lazyTag += tree[index].lazyTag;
tree[rightIndex].sum += (tree[rightIndex].end - tree[rightIndex].start + 1) * tree[index].lazyTag;
tree[index].lazyTag = 0;
}
}
}
在上述代码中,我们实现了分段树的查询和更新操作,并采用lazyTag机制进行延迟传播。
延迟传播是一种优化分段树的常用机制,在进行区间修改操作时,采用lazyTag机制对修改操作进行标记,并延迟执行操作,可以大大提高分段树的更新效率。同时,在进行查询操作时,需要注意将lazyTag更新到下一层子节点,以保证查询结果的正确性。