区间sum查询+区间add
1.
arr是nums对应的数组,区别在于arr0号位置没有元素.
size是arr数组的元素个数,即nums.size()+1.
2.
实现区间sum查询,需要存储区间sum值,所以节点参数需要sum.
如果是静态的区间sum查询这样就行了.如果需要区间add操作,对应的参数是addlazy和isaddlazy.
3.
最后树结构vector<treenode> t.
4.
pushup函数对应的是更新操作,即add操作,当你需要维护i节点sum信息时,维护左孩子+维护右孩子,维护好了用左右孩子信息维护自己.
pushup只需要对应sum信息即可.
意思是用孩子的信息维护自己.
5.
pushdown函数意思是将当前节点的任务下发一层,用旧任务维护一层孩子信息.
当然前提是你当前节点有任务才能够去下发任务.
pushdown下发任务需要维护下一层左右孩子的sum值以及懒更新信息.
当前节点懒信息取消.
6.
add操作,如果当前节点不在任务范围内,直接返回.
如果当前节点范围全在范围内,表示当前节点可以懒住任务,更新当前节点信息+懒信息维护.
如果当前节点有一部分在内部有一部分在外部,递归维护子孩子节点信息,pushup维护当前节点信息.
更新信息操作只主动维护目标位置,否则用孩子信息维护当前节点信息.
7.
query查询操作,如果当前节点不在任务范围内,直接返回.
如果当前节点范围全在范围内,直接返回sum值.
如果当前节点有一部分在内部有一部分在外部,有任务就下发任务,然后在左子树找目标位置,在右子树找目标位置.
class SegmentTree {
private:
int size; // 线段树的大小,根据原始数据的大小调整
vector<int> arr; // 存储原始数据的数组
struct treenode {
int sum; // 该节点所代表的区间的总和
int addlazy; // 懒惰传播时用于加的值
bool isaddlazy; // 是否有懒惰传播的标记
};
vector<treenode> t; // 线段树的节点
// 向上更新节点,维护sum值
void pushup(int i) {
t[i].sum = t[i << 1].sum + t[i << 1 | 1].sum;
}
// 懒惰传播,将当前节点的待更新信息传递给子节点
void pushdown(int i, int ln, int rn) {
if (t[i].isaddlazy) {
// 更新左右子节点的sum
t[i << 1].sum += ln * t[i].addlazy;
t[i << 1 | 1].sum += rn * t[i].addlazy;
// 设置左右子节点的懒惰标记
t[i << 1].isaddlazy = t[i << 1 | 1].isaddlazy = true;
t[i << 1].addlazy = t[i << 1 | 1].addlazy = t[i].addlazy;
// 清除当前节点的懒惰标记
t[i].isaddlazy = false;
}
}
// 区间加操作
void add(int L, int R, int C, int l, int r, int rt) {
if (r < L || R < l) return; // 区间不相交
if (L <= l && r <= R) {
t[rt].sum += (r - l + 1) * C; // 更新区间和
t[rt].isaddlazy = true;
t[rt].addlazy = C; // 设置懒惰标记
return;
}
int mid = (l + r) >> 1;
pushdown(rt, mid - l + 1, r - (mid + 1) + 1); // 下推懒惰标记
add(L, R, C, l, mid, rt << 1);
add(L, R, C, mid + 1, r, rt << 1 | 1);
pushup(rt); // 更新当前节点
}
// 区间查询操作
int query(int L, int R, int l, int r, int rt) {
if (r < L || R < l) return 0; // 区间不相交
if (L <= l && r <= R) {
return t[rt].sum; // 完全包含于查询区间
}
int mid = (l + r) >> 1;
pushdown(rt, mid - l + 1, r - (mid + 1) + 1); // 下推懒惰标记
return query(L, R, l, mid, rt << 1) + query(L, R, mid + 1, r, rt << 1 | 1);
}
// 构建线段树
void build(int l, int r, int rt) {
if (l == r) {
t[rt].sum = arr[l]; // 叶子节点
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
pushup(rt); // 更新当前节点
}
public:
SegmentTree(vector<int> nums) {
size = nums.size() + 1;
t.resize(size << 2); // 分配空间
arr.resize(size);
for (int i = 0; i < nums.size(); i++) {
arr[i + 1] = nums[i]; // 转换原始数组的下标,使其从1开始
}
build(1, size - 1, 1); // 构建线段树
}
// 对外提供的区间加操作接口
void _add(int L, int R, int C) {
L++, R++; // 将0-based转为1-based
add(L, R, C, 1, size - 1, 1);
}
// 对外提供的区间查询操作接口
int _query(int L, int R) {
L++, R++; // 将0-based转为1-based
return query(L, R, 1, size - 1, 1);
}
};
int main() {
vector<int> nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // 初始化数组
SegmentTree t(nums); // 创建线段树实例
// 使用线段树查询全数组的和
cout << t._query(0, nums.size() - 1) << endl; // 应输出45,因为1+2+3+...+9=45
cout << accumulate(nums.begin(), nums.end(), 0) << endl; // 使用STL算法验证上面的输出
// 查询数组中前7个元素的和
cout << t._query(0, 6) << endl; // 输出28,因为1+2+3+...+7=28
cout << accumulate(nums.begin(), nums.begin() + 6 + 1, 0) << endl; // 使用STL算法验证上面的输出
// 对前7个元素的值都加1
t._add(0, 6, 1);
// 再次查询前7个元素的和,现在应该是35,因为(1+1)+(2+1)+...+(7+1)=35
cout << t._query(0, 6) << endl;
// 查询前4个元素的和,现在应该是14,因为(1+1)+(2+1)+(3+1)+(4+1)=14
cout << t._query(0, 3) << endl;
}
#endif // 1
区间sum查询+区间update
class SegmentTree {
private:
struct treenode {
int sum; // 存储当前节点的区间和
bool isupdatelazy; // 标记该节点是否有懒更新
int updatelazy; // 懒更新时用于赋值的数值
};
vector<int> arr; // 原始数据数组
int size; // 线段树数组的大小
vector<treenode> t; // 线段树的节点数组
// 向上更新节点,保持父节点的值是其子节点值的总和
void pushup(int i) {
t[i].sum = t[i << 1].sum + t[i << 1 | 1].sum;
}
// 懒更新传递,将当前节点的更新值传递给子节点
void pushdown(int i, int ln, int rn) {
if (t[i].isupdatelazy) {
// 更新子节点的值和懒更新标记
t[i << 1].sum = ln * t[i].updatelazy;
t[i << 1 | 1].sum = rn * t[i].updatelazy;
t[i << 1].updatelazy = t[i << 1 | 1].updatelazy = t[i].updatelazy;
t[i << 1].isupdatelazy = t[i << 1 | 1].isupdatelazy = true;
// 清除当前节点的懒更新标记
t[i].isupdatelazy = false;
}
}
// 构建线段树,初始化所有节点
void build(int l, int r, int rt) {
if (l == r) {
t[rt].sum = arr[l]; // 叶节点直接取值
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1); // 递归构建左子树
build(mid + 1, r, rt << 1 | 1); // 递归构建右子树
pushup(rt); // 更新父节点的值
}
// 区间查询
int query(int L, int R, int l, int r, int rt) {
if (r < L || R < l) return 0; // 查询区间和当前节点区间不相交
if (L <= l && r <= R) {
return t[rt].sum; // 查询区间完全包含当前节点区间
}
int mid = (l + r) >> 1;
pushdown(rt, mid - l + 1, r - mid); // 下传懒更新
return query(L, R, l, mid, rt << 1) + query(L, R, mid + 1, r, rt << 1 | 1); // 分别查询左右子树
}
// 区间更新
void update(int L, int R, int C, int l, int r, int rt) {
if (r < L || R < l) return; // 更新区间和当前节点区间不相交
if (L <= l && r <= R) {
t[rt].sum = (r - l + 1) * C; // 更新区间完全包含当前节点区间
t[rt].isupdatelazy = true; // 设置懒更新标记
t[rt].updatelazy = C; // 记录懒更新的值
return;
}
int mid = (l + r) >> 1;
pushdown(rt, mid - l + 1, r - (mid + 1) + 1); // 下传懒更新
update(L, R, C, l, mid, rt << 1); // 更新左子树
update(L, R, C, mid + 1, r, rt << 1 | 1); // 更新右子树
pushup(rt); // 更新当前节点
}
public:
// 构造函数,初始化线段
SegmentTree(vector<int> nums) {
size = nums.size() + 1; // 数组大小+1,因为线段树的构建是从1开始索引
t.resize(size << 2); // 线段树的空间是数组大小的4倍
arr.resize(size); // 调整arr的大小,为原始数据预留空间
for (int i = 0; i < nums.size(); i++) {
arr[i + 1] = nums[i]; // 将原始数据从索引1开始存储
}
build(1, size - 1, 1); // 从根节点开始构建线段树
}
// 对外提供的查询接口,用户传入0-based索引,转换为1-based处理
int _query(int L, int R) {
L++, R++; // 将用户的0-based索引转换为1-based
return query(L, R, 1, size - 1, 1); // 执行查询
}
// 对外提供的更新接口,用户传入0-based索引,转换为1-based处理
void _update(int L, int R, int C) {
L++, R++; // 将用户的0-based索引转换为1-based
update(L, R, C, 1, size - 1, 1); // 执行更新
}
};
int main() {
vector<int> nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // 初始化数组
SegmentTree t(nums); // 创建线段树实例
// 使用线段树查询整个区间的和
cout << t._query(0, nums.size() - 1) << endl; // 应该输出45,因为1+2+3+...+9=45
cout << accumulate(nums.begin(), nums.end(), 0) << endl; // 使用STL算法验证上面的输出
// 查询数组中前6个元素的和
cout << t._query(0, 5) << endl; // 输出21,因为1+2+3+4+5+6=21
cout << accumulate(nums.begin(), nums.begin() + 5 + 1, 0) << endl; // 使用STL算法验证上面的输出
// 对前6个元素全部更新为5
t._update(0, 5, 5);
// 再次查询更新后的前6个元素的和,现在应该是30,因为5*6=30
cout << t._query(0, 5) << endl;
// 查询更新后的前4个元素的和,现在应该是20,因为5*4=20
cout << t._query(0, 3) << endl;
}
#endif // 1
区间max查询+区间add
class SegmentTree {
private:
vector<int> arr; // 存储原始数据的数组
int size; // 线段树的大小
struct treenode {
int maxvalue; // 节点存储的区间最大值
int addlazy; // 懒惰增加值,表示需要在这个节点的区间上加上的值
int isaddlazy; // 懒惰标记,表示是否有未下传的增加操作
};
vector<treenode> t; // 线段树的节点数组
// 向上更新节点的最大值
void pushup(int i) {
t[i].maxvalue = max(t[i << 1].maxvalue, t[i << 1 | 1].maxvalue);
}
// 懒惰传播,将当前节点的待增加值传递给子节点
void pushdown(int i) {
if (t[i].isaddlazy) {
t[i << 1].maxvalue += t[i].addlazy;
t[i << 1 | 1].maxvalue += t[i].addlazy;
t[i << 1].isaddlazy = t[i << 1 | 1].isaddlazy = true;
t[i << 1].addlazy += t[i].addlazy;
t[i << 1 | 1].addlazy += t[i].addlazy;
t[i].isaddlazy = false; // 清除当前节点的懒惰标记
}
}
// 构建线段树
void build(int l, int r, int rt) {
if (l == r) {
t[rt].maxvalue = arr[l]; // 叶节点直接取值
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1); // 递归构建左子树
build(mid + 1, r, rt << 1 | 1); // 递归构建右子树
pushup(rt); // 更新父节点的最大值
}
// 区间最大值查询
int _query(int L, int R, int l, int r, int rt) {
if (r < L || R < l) return 0; // 查询区间和当前节点区间不相交
if (L <= l && r <= R) {
return t[rt].maxvalue; // 查询区间完全包含当前节点区间
}
int mid = (l + r) >> 1;
pushdown(rt); // 下传懒更新
return max(_query(L, R, l, mid, rt << 1), _query(L, R, mid + 1, r, rt << 1 | 1)); // 分别查询左右子树的最大值
}
// 区间增加操作
void _add(int L, int R, int C, int l, int r, int rt) {
if (r < L || R < l) return; // 更新区间和当前节点区间不相交
if (L <= l && r <= R) {
t[rt].maxvalue += C; // 更新区间完全包含当前节点区间
t[rt].isaddlazy = true;
t[rt].addlazy += C; // 累加懒惰增加值
return;
}
int mid = (l + r) >> 1;
pushdown(rt); // 下传懒更新
_add(L, R, C, l, mid, rt << 1); // 更新左子树
_add(L, R, C, mid + 1, r, rt << 1 | 1); // 更新右子树
pushup(rt); // 更新当前节点的最大值
}
public:
// 构造函数,初始化线段树
SegmentTree(vector<int> nums) {
size = nums.size() + 1; // 数组大小+1,因为线段树的构建是从1开始索引
t.resize(size << 2); // 线段树的空间是数组大小的4倍
arr.resize(size); // 调整arr的大小,为原始数据预留空间
for (int i = 0; i < nums.size(); i++) {
arr[i + 1] = nums[i]; // 将原始数据从索引1开始存储
}
build(1, size - 1, 1); // 从根节点开始构建线段树
}
// 对外提供的区间增加操作
void add(int L, int R, int C) {
L++, R++; // 将用户的0-based索引转换为1-based
_add(L, R, C, 1, size - 1, 1); // 执行增加操作
}
// 对外提供的区间最大值查询
int query(int L, int R) {
L++, R++; // 将用户的0-based索引转换为1-based
return _query(L, R, 1, size - 1, 1); // 执行查询操作
}
};
int main() {
vector<int> nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // 初始化数组
SegmentTree t(nums); // 创建线段树实例
// 使用线段树查询整个区间的最大值
cout << t.query(0, nums.size() - 1) << endl; // 应输出9,因为9是数组中的最大值
cout << t.query(0, 5) << endl; // 输出6,因为前6个元素的最大值是6
// 对前6个元素增加2
t.add(0, 5, 2);
// 再次查询增加后的前6个元素的最大值,现在应该是8,因为原来的最大值6增加了2
cout << t.query(0, 5) << endl;
// 查询前3个元素的最大值,现在应该是5,因为前3个元素分别增加了2变成了3, 4, 5
cout << t.query(0, 2) << endl;
// 查询前2个元素的最大值,现在应该是4,因为1和2分别增加了2变成了3和4
cout << t.query(0, 1) << endl;
}
#endif // 1
区间max查询+区间update
class SegmentTree {
vector<int> arr; // 存储原始数据的数组
int size; // 数组大小
struct treenode {
int maxvalue; // 当前节点表示的区间内的最大值
int isupdatelazy; // 更新标记
int updatelazy; // 更新的值
};
vector<treenode> t; // 线段树节点数组
void pushup(int i) { // 更新父节点的最大值
t[i].maxvalue = max(t[i << 1].maxvalue, t[i << 1 | 1].maxvalue);
}
void pushdown(int i) { // 将父节点的更新标记向下传递
if (t[i].isupdatelazy) {
t[i << 1].maxvalue = t[i << 1 | 1].maxvalue = t[i].updatelazy;
t[i << 1].isupdatelazy = t[i << 1 | 1].isupdatelazy = true;
t[i << 1].updatelazy = t[i << 1 | 1].updatelazy = t[i].updatelazy;
t[i].isupdatelazy = false;
}
}
void build(int l, int r, int rt) { // 构建线段树
if (l == r) {
t[rt].maxvalue = arr[l]; // 叶节点值为原始数据值
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
pushup(rt);
}
int _query(int L, int R, int l, int r, int rt) { // 查询区间最大值
if (r < L || R < l) return 0;
if (L <= l && r <= R) {
return t[rt].maxvalue;
}
int mid = (l + r) >> 1;
pushdown(rt);
return max(_query(L, R, l, mid, rt << 1), _query(L, R, mid + 1, r, rt << 1 | 1));
}
void _update(int L, int R, int C, int l, int r, int rt) { // 更新区间值
if (r < L || R < l) return;
if (L <= l && r <= R) {
t[rt].maxvalue = C;
t[rt].isupdatelazy = true;
t[rt].updatelazy = C;
return;
}
int mid = (l + r) >> 1;
pushdown(rt);
_update(L, R, C, l, mid, rt << 1);
_update(L, R, C, mid + 1, r, rt << 1 | 1);
pushup(rt);
}
public:
void update(int L, int R, int C) { // 对区间进行更新
L++, R++; // 区间左右边界加一用于匹配数组下标
_update(L, R, C, 1, size - 1, 1);
}
int query(int L, int R) { // 查询区间最大值
L++, R++; // 区间左右边界加一用于匹配数组下标
return _query(L, R, 1, size - 1, 1);
}
SegmentTree(vector<int> nums) { // 构造函数
size = nums.size() + 1; // 设置线段树数组大小
arr.resize(size); // 调整原始数据数组大小
t.resize(size << 2); // 调整线段树节点数组大小
for (int i = 0; i < nums.size(); i++) {
arr[i + 1] = nums[i]; // 将原始数据复制到数组中
}
build(1, size - 1, 1); // 构建线段树
}
};
int main() {
vector<int> nums = { 1,2,3,4,5,6,7,8,9 }; // 原始数据数组
SegmentTree t(nums); // 创建线段树对象
cout << t.query(0, nums.size()) << endl; // 查询整个区间的最大值
cout << t.query(0, 5) << endl; // 查询指定区间的最大值
t.update(0, 5, 3); // 更新指定区间的值为3
cout << t.query(0, 5) << endl; // 再次查询更新后的指定区间的最大值
}
结尾
最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!