线段树
来源:互联网 发布:阿里云 bd 待遇 编辑:程序博客网 时间:2024/06/14 00:44
持续更新中。。。
线段树(1):点修改
用一个数组存节点的附加信息,并不需要存储线段的边界,因为每次递归都是严格二分的,所以每次递归到的节点范围都是确定的。节点数量最大为2*n,但数组大小要开到4*n。因为每一层不一定都是满的,虽然这些节点不存在,但依然在数组中占位置。如果当前节点下标为v,那么它的左孩子下标为v*2,右孩子v*2+1。
本线段树主要包含3个函数:建树,更新,查找。
以查找一段区间内的最大值为例。题目链接:I Hate It
(一)建树
从根节点往下递归,如果左边界(L)==右边界(R),则说明这是叶节点,给这个节点赋值,并回溯更新其所有的祖先节点。
void build(int v,int L,int R){ if(L==R) { int x; scanf("%d",&x); maxv[v]=x; } else { int mid=(L+R)/2; build(v*2,L,mid); build(v*2+1,mid+1,R); maxv[v]=max(maxv[v*2],maxv[v*2+1]); }}
(二)更新
与建树过程相似。用到了二分查找。
void updata(int v,int L,int R,int id,int score){ if(L==R && L==id) { maxv[v]=score; return; } int mid=(L+R)/2; if(id<=mid) updata(v*2,L,mid,id,score); else updata(v*2+1,mid+1,R,id,score); maxv[v]=max(maxv[v*2],maxv[v*2+1]);}
(三)查询
二分查找。int ask(int v,int L,int R,int ql,int qr){ if(ql<=L && R<=qr) return maxv[v]; int mid=(L+R)/2; if(qr<=mid) return ask(v*2,L,mid,ql,qr); else if(ql>mid) return ask(v*2+1,mid+1,R,ql,qr); else if(ql<=mid && mid<=qr) return max(ask(v*2,L,mid,ql,qr),ask(v*2+1,mid+1,R,ql,qr));}
线段树(2):区间修改
区间修改是对区间内的所有节点进行修改,所以如果还用点修改的线段树来做,效率会非常低。这里我们引入了一个延迟更新标记lazy(又称懒惰标记)。顾名思义,这个标记非常懒,更新节点时能停则停,即这个区间包含在需要更新的区间内,就不会更新下面的子节点了。除非需要访问或修改它的子节点,它才会往下更新。为何我想起了检查卫生时的场景?不得不说,这样效率非常高。
代码在点修改线段树的基础上多维护一个lazy数组。以区间求和为例。题目链接:Just a Hook
(一)建树
初始化sumv与lazy。
void build(int v,int L,int R){ lazy[v]=-1; if(L==R) sumv[v]=1; else { int mid=(L+R)/2; build(v*2,L,mid); build(v*2+1,mid+1,R); sumv[v]=sumv[v*2]+sumv[v*2+1]; }}
(二)更新区间
如果访问到当前节点有lazy标记,则pushdown,将标记推到其子节点上去。不要忘了取消当前节点的lazy标记。如果节点的区间包含在要更新的区间内,则更新区间值,加上lazy标记。
void updata(int v,int L,int R,int ql,int qr,int qn){ if(ql<=L && R<=qr) { sumv[v]=qn*(R-L+1); lazy[v]=qn; return; } if(lazy[v]!=-1) pushdown(v,L,R); int mid=(L+R)/2; if(ql<=mid) updata(v*2,L,mid,ql,qr,qn); if(qr>mid) updata(v*2+1,mid+1,R,ql,qr,qn); sumv[v]=sumv[v*2]+sumv[v*2+1];}
(三)pushdown
将lazy标记推到其子节点上,并更新其子节点的sumv值。
void pushdown(int v,int L,int R){ sumv[v*2]=lazy[v]*((R-L+2)/2); lazy[v*2]=lazy[v]; sumv[v*2+1]=lazy[v]*((R-L+1)/2); lazy[v*2+1]=lazy[v]; lazy[v]=-1;}
(四)查询
虽然这一题不需要query,但我还是写了一个模板。就多了一个pushdown。
int query(int v,int L,int R,int ql,int qr){ int ret=0; if(ql<=L && R<=qr) return sumv[v]; int mid=(L+R)/2; if(lazy[v]!=-1) pushdown(v,L,R); if(ql<=mid) ret+=query(v*2,L,mid,ql,qr); if(qr>mid) ret+=query(v*2+1,mid+1,R,ql,qr); return ret;}
阅读全文
0 0
- 线段树?线段树!
- 线段树?线段树!
- 线段_线段树
- 线段_线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- hdu 1878 欧拉回路
- 2017-8-9
- hashMap的底层数据结构:数组+链表
- Scala 入门 WordCount
- react中使用Link在不同路由之间进行参数传值
- 线段树
- C语言(20)素数求和问题
- fork && fork || fork问题
- 看我鼓捣华西安全网(cha.hxsec.com)密码泄露查询接口(有意思的js混淆)
- 第五课记录 Hadoop的起源——Google的基本思想之一
- js中的for循环
- Graphviz绘图的安装与使用
- php 打印函数(echo , print, printf, print_f, var_dump)
- 分分钟教你学会正则表达式