数据结构进阶之线段树

本文深入探讨线段树这一高级数据结构,介绍了线段树的基本操作,包括建树、区间查询、点/区间修改等,并通过例题详细讲解如何维护区间多个信息、使用懒标记优化以及扫描线算法的应用。此外,还列举了多个典型题目,展示了线段树在解决动态维护区间性质问题中的强大能力。
摘要由CSDN通过智能技术生成

0.线段树的介绍及其基本操作

线段树是一种动态维护和查询区间性质的一种高级数据结构,具体定义即简介可见蓝书P210-211。主要操作包括build(建树)、query(区间查询)、modify/update(点/区间修改)、pushup(上传值)、pushdown(下传懒标记)。下面以一道例题来介绍上述的几种操作:
例题0:AcWing 1275.最大数(JSOI2008)
这题还是比较简单,只要建树时开辟最长区间长为m的线段树,然后每次对于A操作,单点修改第n+1个数即可;对于Q操作,查询区间[n-x+1,n]即可。这些都是线段树的基本操作。下面介绍一下基本的函数实现:
线段树数组定义:

struct Node{
   
    int l,r;//左右端点
    int v;//本题中是最大值
}tr[4*N+5];//一般开4倍才不会越界

1.build

void build(int u,int l,int r){
   
    tr[u].l=l,tr[u].r=r;//注意要先存左右端点再结束递归
    if(l==r) return;
    int mid=(l+r)>>1;
    build(u<<1,l,mid);//递归左儿子
    build(u<<1|1,mid+1,r);//递归右儿子
    //pushup(u);这句在这里可以不用加,因为一开始没有数
}

2.pushup

void pushup(int u){
   
    tr[u].v=max(tr[u<<1].v,tr[u<<1|1].v);
}

3.query

int query(int u,int l,int r){
   
	//这里的l,r是查询范围,不是节点的范围
    if(l<=tr[u].l && tr[u].r<=r) return tr[u].v;
    //若整个节点的范围被查询范围包括,直接返回
    int mid=(tr[u].l+tr[u].r)>>1;
    int v=0;
    if(l<=mid) v=query(u<<1,l,r);
    //左儿子与查询范围有交集
    if(r>mid) v=max(v,query(u<<1|1,l,r));
    //右儿子与查询范围有交集
    return v;
}

4.modify

void modify(int u,int x,int d){
   
	//x是要修改的点,d是该节点要改成的值
    if(tr[u].l==tr[u].r){
   
    	//搜到了要修改的单点,直接修改
        tr[u].v=d;
        return;
    }
    int mid=(tr[u].l+tr[u].r)>>1;
    if(x<=mid) modify(u<<1,x,d);//x在左半边
    else modify(u<<1|1,x,d);//x在右半边
    pushup(u);//子区间修改后要更新父区间
}

有了这几种基本的函数,就可以轻松过掉这一个省选题了~
附加例题:AcWing 242.一个简单的整数问题
这题本来是树状数组里的一题,但也可以用线段树做一下练练手。这题就是基本的单点修改和区间查询,非常简单。

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+5;
int a[N+5];
struct Node{
   
    int l,r;
    int d;
}tr[4*N+5];
void pushup(int u){
   
    tr[u].d=tr[u<<1].d+tr[u<<1|1].d;
}
void build(int u,int l,int r){
   
    if(l==r){
   
        tr[u]={
   l,r,a[l]-a[l-1]};
    }
    else{
   
        tr[u]={
   l,r};
        int mid=(l+r)>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
        pushup(u);
    }
}
int query(int u,int l,int r){
   
    if(tr[u].l>=l && tr[u].r<=r) return tr[u].d;
    int mid=(tr[u].l+tr[u].r)>>1;
    int res=0;
    if(l<=mid) res+=query(u<<1,l,r);
    if(r>mid) res+=query(u<<1|1,l,r);
    return res;
}
void modify(int u,int x,int d){
   
    if(tr[u].l==tr[u].r){
   
        tr[u].d+=d;
    }
    else{
   
        int mid=(tr[u].l+tr[u].r)>>1;
        if(x<=mid) modify(u<<1,x,d);
        else modify(u<<1|1,x,d);
        pushup(u);
    }
}
int main(){
   
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    build(1,1,n);
    while(m--){
   
        char op;
        cin>>op;
        if(op=='Q'){
   
            int x;
            scanf("%d",&x);
            printf("%d\n",query(1,1,x));
        }
        else{
   
            int l,r,d;
            scanf("%d%d%d",&l,&r,&d);
            modify(1,l,d);
            if(r+1<=n) modify(1,r+1,-d);
        }
    }
    return 0;
}

P.S.:其实所有的树状数组的题都可以用线段树实现。因为树状数组所支持的操作是单点修改和求前缀和,这恰好对应了线段树所支持的基本操作:单点修改和区间求和。

1.维护区间多个信息的线段树

例题1:AcWing 245.你能回答这些问题吗?
这题要维护的区间信息是最大连续子段和,显然需要用多个不同的信息来维护,那么我们考虑加入左端最大连续子段和、右端最大连续子段和。那么这个信息就可以高效地得到维护了,这种储存多个信息来维护并导出所求信息的方法很常见,在动态规划中也有涉及:如AcWing 283.多边形。代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
const int N=5e5;
int a[N];
struct Node{
   
    int l,r;
    int sum,dat;//sum表示区间和,dat表示最大连续子段和
    int lmax,rmax;//lmax表示区间包括左端点的最大连续子段和,rmax类似
}tr[N*4];
void pushup(Node &a,Node &b,Node &c){
   //要利用图形来想情况
    a.sum=b.sum+c.sum;
    a.lmax=max(b.lmax,b.sum+c.lmax);
    a.rmax=max(c.rmax,c.sum<
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值