线段树的建立,修改,查询,更新 C++

线段树(Segment Tree)是一种用于解决区间查询问题的数据结构。它将一个线性的区间划分成多个较小的子区间,并将每个子区间的信息进行存储和更新。通常情况下,线段树用于解决以下两类问题:

1. 区间查询问题:给定一个区间,需要查询该区间的某个特定信息,如求和、最大值、最小值等。
2. 区间更新问题:给定一个区间,需要对该区间的某个特定信息进行更新操作,如修改某个元素的值、增加某个元素的值等。

线段树的时间复杂度为 O(logN),其中 N 表示区间的长度。由于线段树的构建和查询操作都需要递归地访问每个节点,所以时间复杂度与树的高度有关,而树的高度为 O(logN)。

线段树(Segment Tree)是一种用于解决区间查询的数据结构。它可以高效地支持区间的修改和查询操作。

线段树的建立,修改,查询,更新操作可以分为如下几个步骤:

1. 建立:线段树是一棵完全二叉树,每个节点代表一个区间。在建立线段树时,首先需要定义好线段树的结构。通常将线段树的结构定义为一个数组,数组的长度是原始数组长度的四倍左右,这样可以确保线段树的高度不会超过4 * log(n),其中n是原始数组的长度。然后使用递归的方式,从根节点开始,不断将区间划分成两个子区间,直到区间只包含一个元素。在递归的过程中,可以根据实际需求来决定线段树节点所存储的信息,例如区间的最大值、最小值、和、平均值等。

2. 修改:修改操作用于更新线段树中某个节点代表的区间的值。首先需要找到需要修改的节点,如果当前节点代表的区间正好等于要修改的区间,则直接更新节点的值。否则,根据当前节点代表的区间和要修改的区间的关系,将修改操作转移到当前节点的左子节点或右子节点。

3. 查询:查询操作用于获取线段树中某个区间的信息。首先需要找到代表该区间的节点,如果当前节点代表的区间正好等于要查询的区间,则直接返回节点的值。否则,根据当前节点代表的区间和要查询的区间的关系,将查询操作转移到当前节点的左子节点或右子节点,并将子节点返回的信息合并起来。

4. 更新:更新操作是修改操作的一种特殊情况。当需要对线段树中某个区间的所有节点进行修改时,可以使用更新操作实现。首先需要找到代表该区间的节点,然后递归地将修改操作应用到当前节点的左子节点和右子节点,并更新当前节点的值。

下面就是实现线段树的 C++ 代码:

#include <bits/stdc++.h>
#define ll long long
#define endl "\n"
#define lc p << 1     // 左孩子的下标;
#define rc p << 1 | 1 // 右孩子下标;
#define KUI ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
const int con = 1e5;
const int mod = 998244353;
int n;
int w[con];
struct jgt
{
    int l, r, sum, add;
} tr[con * 4];
void build(int p, int l, int r)
{
    tr[p].l = l; // 该节点p的左边界;
    tr[p].r = r; // 该节点p的右边界;
    if (l == r)
    {
        tr[p].sum = w[l];
        return; // l==r,说明是叶子节点就赋值并返回;
    }
    int m = l + r >> 1;  // 不是叶子节点就裂开;
    build(lc, l, m);     // 建左孩子,build(左孩子树节点,左孩子左边界,左孩子右边界);
    build(rc, m + 1, r); // 建右孩子,build(右孩子树节点,右孩子左边界,右孩子右边界);
    pushup(p);           // 孩子节点建完就更新该节点sum值;
    // 在build(lc, l, m);里面,lc是树的节点,l,r是数组的下标范围;
}
void pushup(int p)
{
    tr[p].sum = tr[lc].sum + tr[rc].sum;
}
void pushdown(int p)
{
    if (tr[p].add)
    {
        tr[lc].sum += tr[p].add * (tr[lc].r - tr[lc].l + 1);
        tr[rc].sum += tr[p].add * (tr[rc].r - tr[rc].l + 1);
        tr[lc].add += tr[p].add;
        tr[rc].add += tr[p].add;
    }
}
void update(int p, int x, int y, int k)
{
    if (x <= tr[p].l && tr[p].r <= y)
    {
        // 当更新的范围包含该节点时,直接返回该节点的值,更新懒标记,不再向下更新,提高效率;
        tr[p].sum += (tr[p].r - tr[p].l + 1) * k;
        tr[p].add += k;
        return;
    }
    int m = tr[p].l + tr[p].r >> 1; // m指的是左右节点的中间值;
    if (tr[p].add)
    {
        pushdown(p);
        // 当需要继续向下更新时,如果发现该节点存在懒标记,则更新孩子节点;
    }
    if (x <= m)
    {
        update(lc, x, y, k);
    }
    if (y > m)
    {
        update(rc, x, y, k);
    }
    pushup(p);
}
int query(int p, int x, int y)
{
    if (x <= tr[p].l && tr[p].r <= y)
    {
        return tr[p].sum;
    }
    int m = tr[p].l + tr[p].r >> 1;
    if (tr[p].add)
    {
        // 当需要继续向下查询时,如果发现该节点存在懒标记,则更新孩子节点;
        pushdown(p);
    }
    int sum = 0;
    if (x <= m)
    {
        sum += query(lc, x, y);
    }
    if (y > m)
    {
        sum += query(rc, x, y);
    }
    return sum;
}
void take()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> w[i];
    }
    build(1, 1, n); // 从根节点1开始建树,build(根节点,左边界,右边界);
    query(1, 1, n); // 查询, query(根节点,查询左边界,查询右边界);
}
int main()
{
    KUI;
    int t1 = 1;
    cin >> t1;
    while (t1--)
    {
        take();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值