树-线段树

线段树基础

线段树是一个完全二叉树,在各个节点(1-n)保存一个线段,由于整体是二叉树的构造,可以保证查询的效率。
在这里插入图片描述
构建出来的树如上所示,每个结点均对应一段线段。
树的结点信息储存在索引为1-7的数组空间里。

例1:求数组中某区间的最小值
很明显我们要先建树,并且在建树回溯的时候把信息赋给各个结点。

#include<iostream>
using namespace std;
int a[100],segTree[100];
//递归建树
void build(int node,int b,int e){
    //到叶子节点直接赋值
    if(b == e){
        segTree[node] = a[b];
    }else{
        //继续递归
        build(2*node,b,(b+e)/2);
        build(2*node+1,(b+e)/2+1,e);
        //回溯的时候做一个'归并'的操作,此处比较了node的叶子结点哪个更小,那个小就取哪个赋给node
        if(segTree[2*node]<=segTree[2*node+1]){
            segTree[node] = segTree[2*node];
        }else{
            segTree[node] = segTree[2*node+1];
        }
    }
}
//[l..b...e....r],查询线段树,此处查询node结点的子树中区间在[b,e]的最小值
int query(int node,int b,int e,int l,int r){
    int p1,p2;
    //不在区间内
    if(l>e||r<b){
        return -1;
    }
    //落入区间,直接返回值
    if(b>=l&&e<=r){
        return segTree[node];
    }
    //否则说明区间还太大,还需要进一步递归划分
    p1 = query(2*node,b,(b+e)/2,l,r);
    p2 = query(2*node+1,(b+e)/2+1,e,l,r);
    //回溯,如果不在左区间那一定在右区间,不在右区间一定在左区间
    if(p1 == -1)
        return p2;
    if(p2 == -1)
        return p1;
    //如果左右区间均返回来值需要比较取更小的值
    if(p1<=p2)
        return p1;
    return p2;
}
//更新操作,对某个点做单点的更新
void update(int node,int b,int e,int i,int add){
    //找到要更新的点了
    if(b == e){
        segTree[node] += add;
        cout<<"renew : "<<segTree[node]<<endl;
    }else{
        //继续二分递归
        int m = (b+e)/2;
        if(i<=m){
            update(2*node,b,m,i,add);
        }else{
            update(2*node+1,m+1,e,i,add);
        }
        //回溯的时候自下而上更新回去,此处依旧取较小值
        segTree[node] = min(segTree[node*2],segTree[node*2+1]);
    }
}
int main(){
    a[0] = 10;a[1] = 2;a[2] = 2;a[3] = 4;a[4] = 2;a[5] = 1;
    //建树,以node = 1结点开始建树,索引范围(0-5)
    build(1,0,5);
    for(int i=0;i<20;i++){
        cout<<segTree[i]<<" ";
    }cout<<endl;
    //查询node = 1结点的子树,(0-5)源数组区间的最大值,数组索引范围(0-5)
    cout<<query(1,0,5,0,5)<<endl;
    //更新node = 1结点的子树,(0-5)源数组区间的索引为0的数组元素的值,减11
    update(1,0,5,0,-11);
    for(int i=0;i<20;i++){
        cout<<segTree[i]<<" ";
    }cout<<endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值