线段树(Segment Tree)简介
来源:互联网 发布:阿里云怎么代理加盟 编辑:程序博客网 时间:2024/06/05 09:39
基本概念
线段树(segment tree)是一种二叉搜索树,它的每一个结点对应着一个区间[l,r],叶子结点对应的是一个单位区间,即l==r。对于一个非叶子结点[l,r],它的左儿子所表示的区间为[l,(l+r)/2],右儿子表示的区间为[(l+r)/2+1,r]。根据定义,线段树是一颗平衡二叉树,它的叶子结点的数目为N,即整个区间的长度。
例如,区间[1,10]的线段树如下图所示:
由于线段树是一颗平衡二叉树,所以它的高度为log级别,这是线段树时间复杂度良好的基础。
线段树的用途较广,主要用于更新和查询。这里的更新和查询一般至少有一个是指区间的更新或查询。由于更新和查询的方法种类比较多,这也决定了线段树的灵活性,针对不同的问题,线段树处理的方式不尽相同。
下面是一个简单的例子:
维护一个数列,每次进行两种操作:
1.修改一个元素
2.查询一段区间的最大值
这是一个经典的RMQ(range minimum/maximum query)问题,用线段树该如何解决?这里更新是点更新,查询是区间查询。
基本操作
可以首先对原序列建立一颗线段树,然后对于更新操作,则对线段树的相应结点进行更新,对于查询操作则进行查询。具体操作如下:
建树:每个节点维护节点所代表区间的左右端点和该区间的信息(上面例子里就是最值)。建树时,如果到了叶子结点,那么这个结点的最值信息就是对应位置数组中的该元素值,否则递归地建左子树和右子树,然后将当前结点的区间最值设置为自己左子树和右子树的较优最值。
修改:一样递归调用,从根节点开始递归到叶子结点并修改叶子结点。回溯时对路径上相应结点的最值进行更新。
查询:还是递归调用。从根节点开始递归查询,如果查询区间在该节点的左子树内,则查询左子树;如果在右子树内,则查询右子树;否则,查询左子树相应区间和右子树相应区间,并将两者的返回值信息(上面例子里就是较大值)返回。
基本操作实现
单点加减 询问区间和
#include<iostream>#include<cstdio>#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1using namespace std;const int maxn=200010;int sum[maxn<<2];void PushUp(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1];}void build(int l,int r,int rt){ if(l==r){ scanf("%d",&sum[rt]); return ; } int m=(l+r)>>1; build(lson); build(rson); PushUp(rt);}void update(int p,int add,int l,int r,int rt){ if(l==r){ sum[rt]+=add;//这里是加减 return ; } int m=(l+r)>>1; if(p<=m) update(p,add,lson); else update(p,add,rson); PushUp(rt);}int query(int L,int R,int l,int r,int rt){ if(L<=l&&r<=R) return sum[rt]; int m=(l+r)>>1; int ret=0; if(L<=m) ret+=query(L,R,lson);//这里是求和,也可以维护最值 if(R>m) ret+=query(L,R,rson); return ret;}
单点覆盖 询问区间最值
#include<iostream>#include<cstdio>#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1using namespace std;const int maxn=200010;int maxx[maxn<<2];void PushUp(int rt){ maxx[rt]=max(maxx[rt<<1],maxx[rt<<1|1]);}void build(int l,int r,int rt){ if(l==r){ scanf("%d",&maxx[rt]); return ; } int m=(l+r)>>1; build(lson); build(rson); PushUp(rt);}void update(int p,int cov,int l,int r,int rt){ if(l==r){ maxx[rt]=cov;//这里是赋值 return ; } int m=(l+r)>>1; if(p<=m) update(p,cov,lson); else update(p,cov,rson); PushUp(rt);}int query(int L,int R,int l,int r,int rt){ if(L<=l&&r<=R) return maxx[rt]; int m=(l+r)>>1; int ret=0;//较小数 if(L<=m) ret=max(ret,query(L,R,lson)); if(R>m) ret=max(ret,query(L,R,rson)); return ret;}
区间加减 询问区间和(区间最值同理)
#include<iostream>#include<cstdio>#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1using namespace std;const int maxn=200010;typedef long long LL;LL add[maxn<<2];//兼顾lazy标记与具体变化LL sum[maxn<<2];void PushUp(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1];}void PushDown(int rt,int len){ if(add[rt]){ add[rt<<1]+=add[rt];//下放,注意是+=而不是直接赋值 add[rt<<1|1]+=add[rt]; sum[rt<<1]+=add[rt]*(len-(len>>1)); sum[rt<<1|1]+=add[rt]*(len>>1); add[rt]=0;//下放了,清除标记 }}void build(int l,int r,int rt){ add[rt]=0;//初始化 if(l==r){ scanf("%lld",&sum[rt]); return ; } int m=(l+r)>>1; build(lson); build(rson); PushUp(rt);}void update(int L,int R,int c,int l,int r,int rt){ if(L<=l&&r<=R){ add[rt]+=c; sum[rt]+=(LL)c*(r-l+1); return ; } PushDown(rt,r-l+1); int m=(l+r)>>1; if(L<=m) update(L,R,c,lson); if(m<R) update(L,R,c,rson); PushUp(rt);}LL query(int L,int R,int l,int r,int rt){ if(L<=l&&r<=R) return sum[rt]; PushDown(rt,r-l+1); int m=(l+r)>>1; LL ret=0; if(L<=m) ret+=query(L,R,lson); if(R>m) ret+=query(L,R,rson); return ret;}
区间覆盖 询问区间和(区间最值同理)
#include<iostream>#include<cstdio>#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1using namespace std;const int maxn=200010;typedef long long LL;LL cov[maxn<<2];//兼顾lazy标记与具体变化LL sum[maxn<<2];void PushUp(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1];}void PushDown(int rt,int len){ if(cov[rt]){ cov[rt<<1]=cov[rt];//下放 cov[rt<<1|1]=cov[rt]; sum[rt<<1]=cov[rt]*(len-(len>>1)); sum[rt<<1|1]=cov[rt]*(len>>1); cov[rt]=0;//下放了,清除标记 }}void build(int l,int r,int rt){ cov[rt]=0;//初始化 if(l==r){ scanf("%lld",&sum[rt]); return ; } int m=(l+r)>>1; build(lson); build(rson); PushUp(rt);}void update(int L,int R,int c,int l,int r,int rt){ if(L<=l&&r<=R){ cov[rt]=c; sum[rt]=(LL)c*(r-l+1); return ; } PushDown(rt,r-l+1); int m=(l+r)>>1; if(L<=m) update(L,R,c,lson); if(m<R) update(L,R,c,rson); PushUp(rt);}LL query(int L,int R,int l,int r,int rt){ if(L<=l&&r<=R) return sum[rt]; PushDown(rt,r-l+1); int m=(l+r)>>1; LL ret=0; if(L<=m) ret+=query(L,R,lson); if(R>m) ret+=query(L,R,rson); return ret;}
以上是一些基本操作,在实际问题中会更灵活。另外还有一些操作,如:区间交并补,离散化,区间合并,扫描线(典型的有矩形面积并,周长并)等
- 线段树(Segment Tree)简介
- 线段树(segment tree)
- 线段树(Segment Tree)
- 线段树(segment tree)
- segment tree(线段树)
- Segment Tree(线段树)
- 线段树(segment tree)
- 线段树(segment tree)
- 线段树(segment tree)
- 线段树(segment tree)
- 线段树(segment tree)
- 线段树(segment tree)
- 线段树(segment tree)
- segment tree(线段树)
- 线段树(segment tree) code
- 浅谈线段树 Segment Tree
- Segment Tree-线段树学习
- XTU1238:Segment Tree(线段树)
- Java关键字final、static使用总结
- 判断链表是否有环,并返回环的起始节点
- 控制台调试android命令
- AtCoder Beginner Contest 070 Transit Tree Path(一道鸡贼的最短路径题)
- 接触qt来第一个坑:Could not create directory "E:\oysl\QT\Error in " Util.asciify("build-untitle
- 线段树(Segment Tree)简介
- Ubuntu14.04kylin下PCL安装及环境配置
- React Native多图层View,实现empty,content,error,loading等界面无缝跳转
- angular4.0 HTTP初级,nodejs搭建服务器,并使用
- android studio 复制项目运行出现Application Installation Failed问题
- mysql 常用约束
- redis--内部结构--sds
- 文件写入对象
- TOMCAT原理详解及请求过程