【六十三】【算法分析与设计】线段树模板,区间sum查询+区间add,区间sum查询+区间update,区间max查询+区间add,区间max查询+区间update

区间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; // 再次查询更新后的指定区间的最大值
}

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妖精七七_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值