初识线段树

本文详细介绍了线段树的基本概念及其应用,通过实例演示如何构建和更新线段树,实现区间查询等功能。适合初学者快速掌握线段树的使用。

虽然学的不多,但是学习线段树的时候,还是让我想起了堆排序,对堆的维护就类似线段树的更新操作。

总之,线段树是一个完全二叉树,利用了二分的思想,大大优化了对于区间的一系列操作


这就是一个最基本的结构,针对这个结构来看一看我们的基础准备

const int max_node = 1 << 19;
const int Max = 2e6 + 10;//针对题目
struct node{
    int value;
    int l,r;
}node[max_node];
int pre[Max];//每个点(区间长度为0时)对应的结构体数组下标)
max_node ->是层数~

value是每一个节点所对应的最大或最小值,依据题目来定

pre[x] 下面用的时候会有注释

开始建一个空树

void build_tree(int i,int l,int r)//为区间[l,r]建立一个以i为祖先的线段树,i为数组下标,我称作结点序号
{
    node[i].l = l;
    node[i].r = r;
    node[i].value = 0;//初始化
    if(l == r)
    {
        pre[l] = i;//能知道某个点对应的序号,为了更新的时候从下往上一直到顶
                    //记录区间1,n每一个之所对应的节点(都是底层节点)编号
                    //方便从下到上的更新和查值2
        return;
    }
    build_tree(i * 2,l,(r + l) / 2);
    build_tree((i * 2) + 1,(r + l) / 2 + 1,r);
}
当l == r时,就说明到了叶要结束了,这时候记录这个叶所对应的编号是几,这样我们可以通过叶索引处底层编号,然后层层修改值进行更新

如果没有到叶,根据二分的思想,层层建树就好

void updatetree(int ri)//换值后的更新
{
    if(ri == 1)return;//找到了最高节点结束更新的操作
    int fi = ri / 2;
    int a = node[fi << 1].value;
    int b = node[(fi << 1) + 1].value;
    node[fi].value = max(a,b);
    updatetree(fi);
}
由下往上的更新,这个例子存的是最大值,跟新完一个节点的值后,找到其父节点进行更新,层层向上,知道祖宗节点

void query(int i,int l,int r)
{
    if(node[i].l == l && node[i].r == r)//所求区间重合的情况
    {
        maX = max(maX,node[i].value);
        return;
    }
    i <<= 1;//访问左右区间注意接受他的值!!
    if(l <= node[i].r)
    {
        if(r <= node[i].r)
        {
            query(i,l,r);
        }
        else
        {
            query(i,l,node[i].r);
        }
    }
    i++;
    if(r >= node[i].l)
    {
        if(l >= node[i].l)
        {
            query(i,l,r);
        }
        else
        {
            query(i,node[i].l,r);
        }
    }
}
判断区间进行查值,如果恰好重合那就return,否则继续二分下去,对于跨区间的查询,再查询过程中,所给区间也会被二分开,然后寻找最大值,最后返回唯一maX

int main()
{
    int n,m,g;
    ios::sync_with_stdio(false);//关闭cin,cout和stdio的同步加快速度
    //达到scanf和printf的速度
    while(cin>>n>>m)
    {
        build_tree(1,1,n);
        for(int i = 1;i <= n;i++)
        {
            cin>>g;
            node[pre[i]].value = g;
            updatetree(pre[i]);
        }
        string op;
        int a,b;
        while(m--)
        {
            cin>>op>>a>>b;
            if(op[0] == 'Q')
            {
                maX = 0;
                query(1,a,b);
                cout<<maX<<endl;
            }
            else
            {
                node[pre[a]].value = b;
                updatetree(pre[a]);
            }
        }
    }
    return 0;
}
针对HDU1754的main,都是最基础的线段树的操作,还是挺容易上手的







评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值