线段树学习小结
来源:互联网 发布:路老膏方 常见网络骗 编辑:程序博客网 时间:2024/06/05 00:18
线段树,嗯,是个好东西,可以高效率解决一些区间问题,一般来讲,对于这些问题,RMQ的效率应该是没有线段树高吧?(我不会RMQ,说错别打我(o´・ェ・`o))
线段树是一棵完全二叉树,主要用于记录区间,执行区间加减,求和,查询最大最小等一系列操作,时间复杂度一般实在
每个区间为left~right。
———————–主要操作————————–
一:建(造线段)树:通过二分区间,当left=right时,便可以赋值了。回溯时,每个非叶子节点记录要求值的区间(最大值,最小值,和什么的。)
图就不给了,地址好长好麻烦,看不顺眼。(原谅我我有强迫症)
procedure maketree(p,l,r:longint);var mid:longint;begin if l=r then tree[p]:=a[l] else begin mid:=(l+r) div 2; maketree(p*2,l,mid); maketree(p*2+1,mid+1,r); if tree[p*2]>tree[p*2+1] then tree[p]:=tree[p*2] else tree[p]:=tree[p*2+1]; end;end;
二:区间查询:假设要查询一个区间[1~8]的数组里区间[1~4]里的最大值,我们知道线段树里除了叶子节点,其他节点都是记录该区间的最大值的那个编号,所以我们二分查找就可以了,当搜到的区间的范围对应,便可以比较,更新答案了。
问:如果我要搜的是[1~6]的最大值会如何?
我们发现了因为线段树的是二叉树,所以会把[1~8]分成[1~4]或[5~8]两个区间,我们要搜[1~4]或[5~8]很简单,那搜[1~6]该怎么办?
也很简单,分开即可,分别搜索[1~4]或[5~6]就可以了。
procedure find(p,l,r,a,b:longint);var mid:longint;begin if (l=a)and(r=b) then begin if ans<tree[p] then ans:=tree[p]; end else begin mid:=(l+r) div 2; if b<=mid then find(p*2,l,mid,a,b) else if mid<a then find(p*2+1,mid+1,r,a,b) else begin find(p*2,l,mid,a,mid); find(p*2+1,mid+1,r,mid+1,b); end; end;end;
三:修改区间内某一个值(单节点修改)
比较简单,二分区间到要的地方,修改区间值即可。
procedure check(p,l,r:longint);var mid:longint;begin if l=r then tree[p]:=y//y是要求改成的值。 else begin mid:=(l+r) div 2; if x<=mid then check(p*2,l,mid) else check(p*2+1,mid+1,r); if tree[p*2]>tree[p*2+1] then tree[p]:=tree[p*2] else tree[p]:=tree[p*2+1]; end;end;
四:修改一段区间的值(区间修改)
重点!也是线段树内涉及到最精华的部分——延迟标记(简称“Lazy标记,懒标记”)。
其实也没什么,就是我们在往下修改时,因为是区间修改,不可能在一次修改时就把全部一个个修改掉,这样和暴力有区别?
所以我们在我们要修改的区间上打下一个标记,告诉我们这个区间要修改但我们还没修改,下一次查询啊,修改的时候,顺便将这有标记的区间修改就是了。
procedure insert(x,l,r,st,en,value:longint);var m:longint;begin if (l=st)and(r=en) then begin inc(tree[x].maxvalue,value);//maxvalue表示为该区间的最大值的位置。 inc(tree[x].add,value);//add表示该区间的lazy标记。 end else begin inc(tree[2*x].maxvalue,tree[x].add);//当我们遇到这个有懒标记的区间,顺便修改。也就是将标记下传。 inc(tree[2*x].add,tree[x].add); inc(tree[2*x+1].maxvalue,tree[x].add); inc(tree[2*x+1].add,tree[x].add); tree[x].add:=0;//注意清空标记,表明这里的区间我们已经修改了。 m:=(l+r)shr 1; if en<=m then insert(2*x,l,m,st,en,value) else if st>m then insert(2*x+1,m+1,r,st,en,value) else begin insert(2*x,l,m,st,m,value); insert(2*x+1,m+1,r,m+1,en,value); end; tree[x].maxvalue:=max(tree[2*x].maxvalue,tree[2*x+1].maxvalue); end;end;
当然,涉及到乘除加减的修改时,注意懒标记是应该用怎样的顺序。
其实线段树挺优秀,但是被人们说成空间复杂度较大,并且时间效率不是在理想中的好,编程复杂度高。
其实可以发现每个部分其实是很相像的,稍微留意些细节便是可以了。
- 线段树学习小结
- 线段树学习小结
- 线段树入门学习小结
- 线段树小结
- 线段树小结
- 线段树小结
- 线段树小结
- 线段树小结
- 线段树小结
- 线段树小结
- 线段树小结
- 线段树小结
- 线段树小结
- 线段树小结
- 线段树小结
- 线段树小结
- 线段树 小结
- 线段树小结
- 携程工作感言(一)(6.30)
- 一个小白从零基础自学Android编程笔记之如何创建菜单
- DataUtilitls.java
- Servlet中获得JSP四大作用域的方法
- 图书管理系统
- 线段树学习小结
- android的样式与主题【普通窗体】
- 嵌入式透明传输应用
- Calendar 类详解
- fio开源工具使用
- mac上加入mysql指令
- iOS --- UIView中的坐标转换
- SVN 版本控制工具
- spring中事物参数与事物隔离级别