Line segment tree C++ implementation

1. The line segment tree array data and structure of this question

        data[]={1,2,-3,5,6,-2,7,1,12,30,-10}, 11 elements.

 Second, each function and structure

(1) Line segment tree structure

        Create the structure of the line segment tree, l and r are the left and right boundaries, maxV and minV are the maximum and minimum values, sum is sum, and tag is lazyTag.

typedef struct SegNode{
    int l,r,maxV,minV,sum,tag;
    struct SegNode *pL, *pR;
}SegNode, *SegTree;

#define OK 1
#define ERROR 0

(2) Line segment tree creation

        To create a line segment tree, if you want to change the value of the pointer variable, you need to pass the address of the pointer variable SegTree into it . Since the double pointer represents the pointer of the pointer (the address of the address), we use the double pointer here to use *SegTree to point to the address of the SegTree .

        Created recursively, the exit is recursive to the left when bounded = bounded.

void BuildSegTree(SegTree* root, int l, int r, int data[]){
    //当l == r时,左右边界为相同位置,递归结束
    if(l == r){
        (*root) = new SegNode;
        (*root)->sum = data[l]; //只有一个数,所以和就是这个数
        (*root)->maxV = data[l]; //同上
        (*root)->minV = data[l]; //同上
        (*root)->tag = 0; //新结点没有“欠债”
        (*root)->l = l;
        (*root)->r = r;
        (*root)->pL = NULL; //左右子树为空
        (*root)->pR = NULL;
        return;
    }
    //左右边界不相同时,创建该结点后,分成左右两个子树继续递归
    else{
        (*root) = new SegNode;
        (*root)->l = l;
        (*root)->r = r;
        (*root)->tag = 0;
        int mid = l + (r-l)/2; //求中间位置
        //递归调用该函数创建左右子树
        BuildSegTree(&((*root)->pL), l, mid, data);
        BuildSegTree(&((*root)->pR),mid+1, r, data);
        (*root)->sum = (*root)->pL->sum + (*root)->pR->sum; //通过两个子树的和求该树的和
        (*root)->maxV = max((*root)->pL->maxV, (*root)->pR->maxV); //通过两个子树的最大值比较求该树的最大值
        (*root)->minV = min((*root)->pL->minV,(*root)->pR->minV); //通过两个子树的最小值比较求该树的最小值
    }
}

(3) Clearing lazy marks

        The function of clearing the "debt" of lazy marking. Lazy marking is an operation that can greatly improve efficiency. The usage is that every time the value of a certain range of data is changed, only the current node is changed first, and the value to be changed exists in the laziness of the current node. In the tag, when other methods need to be recursed to the lower node in the future, the lazy tag is passed to the subtree and the value of the subtree is changed.

//清算懒标记“债务”的函数
void LateUpdate(SegTree root){
    //没债务返回就行
    if(root->tag == 0)
        return;
    //左子树不为空,把债务传递给左子树,并且更改左子树的值
    if(root->pL != NULL){
        root->pL->tag += root->tag;
        root->pL->sum += (root->pL->r - root->pL->l+1)*root->tag;
        root->pL->maxV += (root->tag);
        root->pL->minV += (root->tag);
    }
    //右子树同上
    if(root->pR != NULL){
        root->pR->tag = root->tag;
        root->pR->sum = (root->pL->r - root->pL->l+1)*root->tag;
        root->pR->maxV += (root->tag);
        root->pR->minV += (root->tag);
    }
    root->tag = 0; //债务结算完清空
}

(3) Query operation

        Query the maximum, minimum, and . Recursive operation, the exit is that the query range is exactly equal to the current root range. There are three situations in the recursive process: 1. On the left side of mid. 2. On the right side of mid. 3. Both sides.

//查询操作
int query(SegTree root, int l, int r, int *tsum, int *tmax, int *tmin){
    //如果要查询的左边界范围大于右边界或者相反,返回
    if(r < root->l || l > root->r)
        return ERROR;
    //如果刚好查询的范围与当前root的范围相等
    if(l == root->l && r == root->r){
        *tsum = root->sum;
        *tmax = root->maxV;
        *tmin = root->minV;
        return OK;
    }
    LateUpdate(root); //运行到这说明要向子树递归,所以先将债务传递给子树
    int mid = root->l + (root->r - root->l)/2;
    //整个在mid左边的情况
    if(r<=mid){
        query(root->pL, l, r, tsum, tmax, tmin);
        return OK;
    }
    //整个在mid右边的情况
    else if(l >= mid+1){
        query(root->pR, l, r, tsum, tmax,tmin);
        return OK;
    }
    //被mid从中间分开的情况
    else{
        int lsum, rsum, lmax,rmax, lmin, rmin;
        query(root->pL, l, mid, &lsum, &lmax, &lmin);
        query(root->pR, mid+1, r, &rsum, &rmax, &rmin);
        *tsum = lsum+rsum;      //计算左右子树和
        *tmax = max(lmax,rmax); //从左右子树中找最大值
        *tmin = min(lmin,rmin); //从左右子树中找最小值
        return OK;
    }
}

(4) Increase each number in the range by value

//在一个范围内将每个数增加value
void Add(int tl, int tr, SegTree root, int Value){
    //判断是否越界
    if(tl > root->r || tr < root->l){
        return;
    }
    //如果包含在内了,就把所有的值修改,并给tag写上债务
    if(tl <= root->l && tr >= root->r){
        root->sum += (root->r - root->l+1)*Value;
        root->maxV += Value;
        root->minV += Value;
        root->tag += Value;
        return;
    }
    //如果没包含,那就先将债务传递到子树,然后递归
    LateUpdate(root);
    int mid = root->l+(root->r-root->l)/2;
    //都在左侧的情况
    if(tr <= mid)
        Add(tl, tr, root->pL, Value);
    //都在右侧的情况
    else if(tl >= mid+1)
        Add(tl, tr, root->pR, Value);
    //两边都有
    else{
        Add(tl, mid, root->pL, Value);
        Add(mid+1, tr, root->pR, Value);
    }
    //计算各值
    root->sum = root->pL->sum + root->pR->sum;
    root->maxV = max(root->pL->maxV, root->pR->maxV);
    root->minV = min(root->pL->minV, root->pR->minV);
}

(5) Destroy the segment tree

void DestroySegTree(SegTree *root){
    if(*root == NULL){
        return;
    }
    if((*root)->pL == NULL && (*root)->pR == NULL){
        delete *root;
        *root = NULL;
    }
    else{
        if((*root)->pL != NULL){
            DestroySegTree(&((*root)->pL));
        }
        if((*root)->pR != NULL){
            DestroySegTree(&((*root)->pR));
        }
        delete(*root);
    }
}

3. All codes

#include <iostream>
#include <map>
#include <math.h>
#include <cctype>
using namespace std;

//创建线段树的结构, l、r为左边界和右边界,maxV和minV为最大值和最小值,sum为和,tag为lazyTag
typedef struct SegNode{
    int l,r,maxV,minV,sum,tag;
    struct SegNode *pL, *pR;
}SegNode, *SegTree;

#define OK 1
#define ERROR 0
//创建一个线段树,segTreesh
void BuildSegTree(SegTree* root, int l, int r, int data[]){
    //当l == r时,左右边界为相同位置,递归结束
    if(l == r){
        (*root) = new SegNode;
        (*root)->sum = data[l]; //只有一个数,所以和就是这个数
        (*root)->maxV = data[l]; //同上
        (*root)->minV = data[l]; //同上
        (*root)->tag = 0; //新结点没有“欠债”
        (*root)->l = l;
        (*root)->r = r;
        (*root)->pL = NULL; //左右子树为空
        (*root)->pR = NULL;
        return;
    }
    //左右边界不相同时,创建该结点后,分成左右两个子树继续递归
    else{
        (*root) = new SegNode;
        (*root)->l = l;
        (*root)->r = r;
        (*root)->tag = 0;
        int mid = l + (r-l)/2; //求中间位置
        //递归调用该函数创建左右子树
        BuildSegTree(&((*root)->pL), l, mid, data);
        BuildSegTree(&((*root)->pR),mid+1, r, data);
        (*root)->sum = (*root)->pL->sum + (*root)->pR->sum; //通过两个子树的和求该树的和
        (*root)->maxV = max((*root)->pL->maxV, (*root)->pR->maxV); //通过两个子树的最大值比较求该树的最大值
        (*root)->minV = min((*root)->pL->minV,(*root)->pR->minV); //通过两个子树的最小值比较求该树的最小值
    }
}

//先序遍历输出测试
void preOrder(SegTree root){
    if(root == NULL){
        return;
    }
    cout << "(" << root->l << "->" << root->r << ") sum: " << root->sum << " maxV: " << root->maxV << " minV: " << root->minV << ' ' <<endl;
    preOrder(root->pL);
    preOrder(root->pR);
}

//清算懒标记“债务”的函数
void LateUpdate(SegTree root){
    //没债务返回就行
    if(root->tag == 0)
        return;
    //左子树不为空,把债务传递给左子树,并且更改左子树的值
    if(root->pL != NULL){
        root->pL->tag += root->tag;
        root->pL->sum += (root->pL->r - root->pL->l+1)*root->tag;
        root->pL->maxV += (root->tag);
        root->pL->minV += (root->tag);
    }
    //右子树同上
    if(root->pR != NULL){
        root->pR->tag = root->tag;
        root->pR->sum = (root->pL->r - root->pL->l+1)*root->tag;
        root->pR->maxV += (root->tag);
        root->pR->minV += (root->tag);
    }
    root->tag = 0; //债务结算完清空
}

//查询操作
int query(SegTree root, int l, int r, int *tsum, int *tmax, int *tmin){
    //如果要查询的左边界范围大于右边界或者相反,返回
    if(r < root->l || l > root->r)
        return ERROR;
    //如果刚好查询的范围与当前root的范围相等
    if(l == root->l && r == root->r){
        *tsum = root->sum;
        *tmax = root->maxV;
        *tmin = root->minV;
        return OK;
    }
    LateUpdate(root); //运行到这说明要向子树递归,所以先将债务传递给子树
    int mid = root->l + (root->r - root->l)/2;
    //整个在mid左边的情况
    if(r<=mid){
        query(root->pL, l, r, tsum, tmax, tmin);
        return OK;
    }
    //整个在mid右边的情况
    else if(l >= mid+1){
        query(root->pR, l, r, tsum, tmax,tmin);
        return OK;
    }
    //被mid从中间分开的情况
    else{
        int lsum, rsum, lmax,rmax, lmin, rmin;
        query(root->pL, l, mid, &lsum, &lmax, &lmin);
        query(root->pR, mid+1, r, &rsum, &rmax, &rmin);
        *tsum = lsum+rsum;      //计算左右子树和
        *tmax = max(lmax,rmax); //从左右子树中找最大值
        *tmin = min(lmin,rmin); //从左右子树中找最小值
        return OK;
    }
}

//在一个范围内将每个数增加value
void Add(int tl, int tr, SegTree root, int Value){
    //判断是否越界
    if(tl > root->r || tr < root->l){
        return;
    }
    //如果包含在内了,就把所有的值修改,并给tag写上债务
    if(tl <= root->l && tr >= root->r){
        root->sum += (root->r - root->l+1)*Value;
        root->maxV += Value;
        root->minV += Value;
        root->tag += Value;
        return;
    }
    //如果没包含,那就先将债务传递到子树,然后递归
    LateUpdate(root);
    int mid = root->l+(root->r-root->l)/2;
    //都在左侧的情况
    if(tr <= mid)
        Add(tl, tr, root->pL, Value);
    //都在右侧的情况
    else if(tl >= mid+1)
        Add(tl, tr, root->pR, Value);
    //两边都有
    else{
        Add(tl, mid, root->pL, Value);
        Add(mid+1, tr, root->pR, Value);
    }
    //计算各值
    root->sum = root->pL->sum + root->pR->sum;
    root->maxV = max(root->pL->maxV, root->pR->maxV);
    root->minV = min(root->pL->minV, root->pR->minV);
}

//销毁线段树
void DestroySegTree(SegTree *root){
    if(*root == NULL){
        return;
    }
    if((*root)->pL == NULL && (*root)->pR == NULL){
        delete *root;
        *root = NULL;
    }
    else{
        if((*root)->pL != NULL){
            DestroySegTree(&((*root)->pL));
        }
        if((*root)->pR != NULL){
            DestroySegTree(&((*root)->pR));
        }
        delete(*root);
    }
}

int main(){
    SegTree root = NULL; //创建线段树
    int data[]={1,2,-3,5,6,-2,7,1,12,30,-10}; //输入一些数据
    int ssum, smax, smin;
    BuildSegTree(&root, 0, 10, data);  //构建线段树
    preOrder(root);  //先序遍历测试

    query(root, 0, 7, &ssum, &smax, &smin); //求0~7的和
    cout << endl << "0~7的和为:" << ssum << ' ' << smax << ' ' << smin << endl << endl;

    Add(0, 7, root, 3); //给0~7增加3
    query(root, 0, 7, &ssum, &smax, &smin); //求0~7的和
    preOrder(root);
    cout << endl << "加3后,0~7的和为:" << ssum << ' ' << smax << ' ' << smin << endl;

    return 0;
}

4. Running results

 

Guess you like

Origin blog.csdn.net/weixin_63484669/article/details/130783544