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

  线段树就要比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!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值