线段树详解【几个易错点】【功能】

  线段树就要比RMQ算法高级了,上午刚学完RMQ下午公关了线段树就来写一下自己的认知了。

对于线段树,就是一个由根结点往下出发的过程,把最最最上头的那个根结点定义为序号1,可以知道每个根结点(序号为rt、范围l~r)的左子树序号都为rt<<1(即rt*2)且他们的右子树节点都为rt<<1|1(即rt*2+1),然后左右子树平分根结点的长度,即左子树的范围为l~mid,右子树的范围为mid+1~r

然后我上一下我的模版:

建树模版:

void build(int l,int r,int rt)      //l、r指的是根的左右范围,rt指的是根的节点序号
{
    if(l==r) mx[rt] = a[l];         //叶子结点,无根结点了
    else
    {
        int mid = (l+r)>>1;
        build(l,mid,rt<<1);         //树的左子树的序号等于根*2
        build(mid+1,r,rt<<1|1);     //树的右子树的序号等于根*2+1
        mx[rt] = max(mx[rt<<1],mx[rt<<1|1]);        //得到值
    }
}

查询模版:

int query(int l,int r,int rt,int ql,int qr)     //返回ql~qr区间的最大值(l、r指的是原区间)
{
    if(ql==l&&qr==r) return mx[rt];     //此时返回这一节点的值
    else
    {
        int mid = l+r>>1;
        if(qr<=mid) return query(l,mid,rt<<1,ql,qr);        //不在范围,直接取另一条
        else if(ql>mid) return query(mid+1,r,rt<<1|1,ql,qr);        //不在范围,直接取另一头
        else return max(query(l,mid,rt<<1,ql,mid),query(mid+1,r,rt<<1|1,mid+1,qr));     //范围之内,比较
    }
}

这里有这样一个函数很容易被写错:

max(query(l,mid,rt<<1,ql,mid),query(mid+1,r,rt<<1|1,mid+1,qr));

记住qr和ql的更新!!!

我T过几次都是因为此处的qr和ql都忘记了要使用新的值,不然永远都找不到停止条件(ql==l && qr==r)。

更新点模版:

void update(int l,int r,int rt,int p,int v)     //更新了某一节点,则需要重新赋值
{
    if(l==r) mx[rt] = v;        //叶子结点,直接赋值
    else
    {
        int mid = l+r>>1;
        if(p<=mid)update(l,mid,rt<<1,p,v);
        else update(mid+1,r,rt<<1|1,p,v);
        mx[rt] = max(mx[rt<<1],mx[rt<<1|1]);
    }
}

还有一些需要注意的点:

譬如开了一个mx的数组记录树节点,我们需要把mx开的足够大,至少得保证不会RE!

猜你喜欢

转载自blog.csdn.net/qq_41730082/article/details/81261640
今日推荐