线段树-单点更新-hihoCoder1077

2 篇文章 0 订阅
2 篇文章 0 订阅

线段树

线段树是一种二叉搜索树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,它基本能保持每个操作的时间复杂度为O(lgN)。

假定根结点是长度为2^h的区间,第i层有2^i个结点(层数从0开始),每个结点对应一个长度为2^(h-i)的区间。最大层编号为h,所以结点总数为1+2+4+……+2^h=2^(h+1)-1;所以一般线段树开结构体时需要两倍空间。
当整个区间长度不是2的整数幂时,叶子不全在同一层,但树的最大层编号和结点总数仍满足上述结论。
对于线段树中的每一个下标为i非叶子节点[a,b],它的左儿子下标为2*i表示的区间为[a,(a+b)/2],右儿子下标为2*i+1表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。

可以边看下面的图边理解我说的话。

把上面的图和话理解了基本就了解线段树了,接下来看看线段树的代码。
线段树有单点更新和区间更新两种情况,我以hihoCoder1077为例讲一下线段树的单点更新。

hihoCoder

题目链接

这题就是线段树单点更新的裸题。就是可以动态修改区间中的一个数,动态询问区间最小值。具体看代码注释

#include<bits/stdc++.h>
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 1e6+5;
//一般线段树需要开两倍空间,这里1e6大概是2的20次方,所以我开2^21
const int MAX = 1<<21; 
//node_id[i]是区间中第i个数在线段树中的节点编号
int n,q,a,b,c,ans,node_id[maxn];

struct segment_tree{
    int l,r,w;  //l,r区间左右边界,w区间最小值
}tree[MAX];

void build_segment_tree(int i,int l,int r) //建立线段树
{
    tree[i].l=l;
    tree[i].r=r;
    tree[i].w=INF;
    if(l==r) //叶子节点
    {
        node_id[l] = i; //保存叶子节点在线段树中的节点编号
        return;
    }
    //从该节点向下建立线段树
    build_segment_tree(i*2,l,(l+r)/2);
    build_segment_tree(i*2+1,(l+r)/2+1,r);
}

void update_segment_tree(int i) //线段树单点更新(从下向上更新)
{
    if(i==1) return;
    int tmp = i/2;
    tree[tmp].w = min(tree[tmp*2].w,tree[tmp*2+1].w);
    update_segment_tree(tmp);
}

int query_segment_tree(int i,int l,int r) //线段树查询区间最小值
{
    if(tree[i].l==l&&tree[i].r==r) return ans = min(tree[i].w,ans);
    if(l<=tree[i*2].r)
    {
        //查询区间全部在该节点的左子节点区间中
        if(r<=tree[i*2].r) query_segment_tree(i*2,l,r);
        //查询区间包含该节点的左右子节点区间
        else
        {
            query_segment_tree(i*2,l,tree[i*2].r);
            query_segment_tree(i*2+1,tree[i*2+1].l,r);
        }
    }
    //查询区间全部在该节点的右子节点中
    else query_segment_tree(i*2+1,l,r);
}

int main()
{
    scanf("%d",&n);
    build_segment_tree(1,1,n);
    for(int i=1;i<=n;i++)
    {
        //叶子节点的最小值置为它本身的值
        scanf("%d",&tree[node_id[i]].w);
        //从叶子节点向上更新线段树
        update_segment_tree(node_id[i]);
    }
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d%d",&a,&b,&c);
        if(a==0)
        {
            ans = INF;
            query_segment_tree(1,b,c);//查询区间最小值
            printf("%d\n",ans);
        }
        else
        {
            tree[node_id[b]].w = c;//动态修改节点最小值
            update_segment_tree(node_id[b]);
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值