线段树的创建,区间查询,单点更新代码展示(以求和为例)

 线段树这个东西真的够奇怪的。。

线段数的节点表示的是一段区间的值,它要求区间之间必须满足能够相加的条件,不然不能用线段树表示。

线段树包括4中操作,包括创建,区间查询,单点更新和区间更新,而区间更新是一个最难的部分,需要用到标记。。

这里就说一下前三种相对简单的操作。

在这之前,补充两个知识点:

x>> 1 表示的是x/2;

x<<1 表示的是x*2;

|运算: 与偶数进行运算时必在基础上+1,与奇数运算结果不变

创建:

创建的时候是从一开始的区间开始划分,通过二分的思想,将一个大区间分成两个小区间。当左区间等于右区间时,就可已将他看成单个点进行处理了。然后进行回溯,从下往上求出各个节点的值。

代码如下:

//建树
void build (int l,int r,int re) //[l,r]表示节点表示的区间,re表示节点标号
{
    if(l==r)
    {
        tree[re]=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build (l,mid,re<<1);  //将区间二分
    build (mid+1,r,re<<1|1);
    //回溯求出根节点的值
    Pushup (re);
}

 上面有个Pushup,这个函数是给根节点赋值的

它的代码如下:

//更新根节点的值
void Pushup (int re) //求出根节点的值
{
   tree[re]=tree[re<<1]+tree[re<<1|1];
}

下一个就是单点更新了。

单点更新相当于就是对单个左右区间相等的节点的值进行修改 ,这实际上就是从上往下找要修改的单点,修改完之后进行回溯,

依次往上更新根节点的值。

代码如下:

//单点更新
void add (int loc,int data,int l,int r,int re) //loc表示更新的位置,data表示加上的值
{
    //表示找到这个单点了
    if(l==r)
    {
        tree[re]+=data;
    }
    int mid=(l+r)>>1;
    //如果小于中间值,那么肯定在左子树那里,不小于的话就去右子树找了
    if(loc<=mid)
        add (loc,data,l,mid,re<<1);
    else
        add (loc,data,mid+1,r,re<<1|1);
    //别忘记也要更新根节点的值哦
    Pushup(re);
}

 区间查询:

这一部分就有点难了。

区间查询的意思就是给定一个区间查询这个区间的信息。

让我们想想, 如果节点所表示的区间完全包含在所要查询的区间的话,那么我们可以直接返回它的值就可以了。

如果超出了查询范围了的话, 我们就要进行取舍了。

( 1)如果节点的中点mid大于所要查询的左区间left的话, 那么必定该区间中点左边必定有一部分是在查询区间, 这样我们就可以查询节点的左子树了。千万不能也查询右子树,因为mid与right的相对大小不一定, 如果mid>right那么就返回错误值了。 。

( 2)如果mid<right的话, 与上面同理,查询右子树。

代码如下:

//区间查询
int que (int left,int right,int l,int r,int re) //[left,right]表示要查询的区间
{
    if(l>=left&&r<=right)  //说明此时节点所代表的区间都在查询区间之内,可以直接返回了
        return tree[re];
    int mid=(l+r)>>1;
    //通过ans记录区间之和.
    int ans=0;
    //走到这里,说明区间超出查询范围了,需要进行筛选
    //如果区间终点大于等于left,说明该区间左边的需要纳入查询范围
    if(mid>=left)
    {
       ans+=que (left,right,l,mid,re<<1);
    }
    //如果区间终点小于right,说明该区间的右边需要纳入查询范围
    if(mid<right)
    {
        ans+=que (left,right,mid+1,r,re<<1|1);
    }
    return ans;
}

猜你喜欢

转载自blog.csdn.net/qq_41410799/article/details/81606366