线段树之建树,单点更新以及区间查询

     线段树之建树,单点更新以及区间查询

      线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
     使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。

      在这篇文章中,我们就主要讲一下最基础的线段树的创建,单点更新以及区间查询。当然具体操作是以区间和的形式讲解,当遇到其他题目如求最大值或最小值时,只需要修改一部分就行了,思想都是一样的。

      在贴代码之前我想说一下自己关于线段树操作的一些细节,可以说在大多关于线段树讲解的书上,建树的时候总是喜欢先开一个结构体来储存信息,这倒没什么,关键是还要定义两个变量来记录该节点的左右区间的端点,个人觉得这非常没有必要,本来建树开数组的时候大小要为当前给你得点数的4倍,并且在ACM竞赛中点数的数量级非常大,再去定义两个变量浪费了很多空间,关于区间的端点问题,可以在函数的参数列表中做为参数传下去,这样就不用再额外开空间来储存这两个变量了。

      下面将用代码来解释相关的操作,有注释作为说明,这只是作为借鉴用处,如果有不对的地方还请留言,共同进步。

      

const int maxn=10005;//点的个数
int tree_sum[maxn<<2]//建立大小为点个数4倍的数组来储存该区间的和

//建树操作
void bulid_tree_sum(int root,int l,int r)//root表示该节点的下标值,l、r分别表示区间的左右端点
{
    if(l==r)//如果是叶子节点,就赋值
    {
        cin>>tree_sum[root];
        return ;
    }

    int mid=(l+r)>>1;

    build_tree_sum(root<<1,l,mid);//建立左儿子
    build_tree_sum(root<<1|1,mid+1,r);//建立右儿子

    tree_sum[root]=tree_sum[root<<1]+tree_sum[root<<1|1];//更新父亲节点
}

//单点更新操作
void update(int root,int l,int r,int pos,int val)//pos表示区间的哪个点有更新,val表示更新后的值
{
    if(l==r&&l==pos)//如果找到该叶子节点就更新
    {
        tree_sum[root]=val;
        return ;
    }

    int mid=(l+r)>>1;

    if(pos<=mid)
        update(root<<1,l,mid,pos,val);//如果满足条件查找左儿子
    else
        update(root<<1|1,mid+1,r,pos,val);//否则查找右儿子

    tree_sum[root]=tree_sum[root<<1]+tree_sum[root<<1|1];//同时更新父亲节点
}

//区间和查询操作
int query(int root,int l,int r,int L,int R)//L,R分别表示要查询的区间的左右端点
{
    if(L<=l&&r<=R)//如果所求区间包含该节点所表示的区间就返回该区间的和,不用再去访问他的儿子节点
    {
        return tree_sum[root];
    }

    int mid=(l+r)>>1,sum=0;

    if(L<=mid)//如果所求区间有一部分在该节点左儿子的范围内,就查询左儿子
        sum+=query(root<<1,l,mid,L,R);
    if(mid

你可能感兴趣的:(ACM-线段树系列)