线段树
来源:互联网 发布:win10修改网卡mac地址 编辑:程序博客网 时间:2024/06/07 16:37
线段树
1 建树
以求区间最大值为例
a数组为构造线段树的数组
root 为线段树的树根
star 为数组起始下表
end 为数组终点下表
开始 root = 1,star = 1,end = n;
void build(int root,int star,int end) { if(star==end) { int k; scanf("%d",&k); stu[root] = k;return ; } int mid = (star+end)/2; build(root*2,star,mid); build(root*2+1,mid+1,end); stu[root]=max(stu[2*root],stu[2*root+1]); }
2 单节点更新
root 为线段树的树根
star 为数组起始下表
end为数组终点下表
i 为数组中要更新的位置
add 为这个节点 要更新的值;
void updet(int root,int star,int end,int i,int add) { if(star==end&&star==i) {stu[root]=add;return ;} int mid=(star+end)/2; int t1; if(i<=mid) //和二分查找差不多; updet(2*root,star,mid,i,add); else updet(2*root+1,mid+1,end,i,add); stu[root] = max(stu[2*root],stu[2*root+1]); }
3 查询线段树
root 为线段树树根
star 为数组起始下表
end 为数组终点下表
i 为要查询的区间起始下表
j 为要查询的区间终点下表
这个另一道题,查询区间和的,记住只是查询,不能改变原建树的内容;
而下面这个写的也不好,可能会出错,runtime erorr(栈溢出)
int Query(int root,int star,int end,int i,int j) // 只查询,查询区间值; { if(i>end||j<star) { return 0; } if(i<=star&&end<=j) return stu[root].sum; int mid = (star+end)/2; int t1 = Query(root*2,star,mid,i,j); int t2 = Query(root*2+1,mid+1,end,i,j); //stu[root].sum=t1+t2; //千万不要再重复赋值,本来在更新值时都算好了, //你在更新有啥意义,还把原来的值给覆盖了; // 直接return 找到的两者的和就行了; return t1+t2; }
这个是查询区间最大值的,避免了runtime erorr
int Query(int root,int star,int end,int i,int j) { if(i<=star&&end<=j) return stu[root]; int mid = (star+end)/2; int ret=0;if(i<=mid) // 减少递归; ret=Query(root*2,star,mid,i,j); if(j>mid)// 减少递归;(避免 runtime erorr 栈溢出) ret=max(ret,Query(root*2+1,mid+1,end,i,j)); //这用的挺好; return ret; }
4 线段树区间更新 (这个以给出一个区间,给出一个数,区间中所有值,都变为了这个数)
这个要引出新的东西,“延迟标记”,,这个标记非常懒,更新节点时能停则停,即这个区间包含在需要更新的区间内,就不会更新下面的子节点了。除非需要访问或修改它的子节点,它才会往下更新。不得不说,这样效率非常高。
struct node{int abadd;//延迟标记 int sum;}stu[3*Max];
(1) 建树
初始数组里面都为1,前两个数代表的数区间,第三个数是这个区间里的所有数变为这个数;
和上面建树过程一样,唯一的区别就是把节点的所有的延迟标记都初始为0
void build(int root,int star,int end) //建线段树 { stu[root].abadd = 0; //延迟标记初始为0; if(star==end) { stu[root].sum = 1; return ; } int mid = (star+end)/2; build(2*root,star,mid); build(2*root+1,mid+1,end); stu[root].sum = stu[root*2].sum+stu[root*2+1].sum; }
(2) 延迟标记传给左右子节点,自己的清零
void down_updat(int root,int star,int end) //延迟标记传给左右子节点 { if(stu[root].abadd !=0) { int k=stu[root].abadd; int mid = (star+end)/2; stu[2*root].abadd = k; stu[2*root+1].abadd = k; stu[2*root].sum = (mid-star+1)*k; // 左右孩子的值 也得更新; stu[2*root+1].sum = (end-mid)*k; stu[root].abadd = 0; //自己的延迟标记清0 } }
(3) 查询 区间
int Query(int root,int star,int end,int i,int j) // 只查询,查询区间值; { if(i>end||j<star) { return 0; } if(i<=star&&end<=j) return stu[root].sum; down_updat(root,star,end); //传延迟标记 int mid = (star+end)/2; int t1 = Query(root*2+1,star,mid,i,j); int t2 = Query(root*2+2,mid+1,end,i,j); //stu[root].sum=t1+t2; //千万不要再重复赋值,本来在更新值时都算好了, //你在更新有啥意义,还把原来的值给覆盖了; // 直接return 找到的两者的和就行了; return t1+t2; }
(4) 更新区间值
root 线段树树根
star 数组的起始下表
end 数组的终点下表
s1 要更新的区间起始下表
e1 要更新的区间终点下表
add 区间中全部要更新为的值
下面程序 只是找到当前区间的结点,更新这个节点值,这个节点上加个延迟标记,它的子孙节点只有访问了才更新,不访问就不更新(这也是这个程序的高效性,不清楚的,看上面 “延迟标记” 定义)
int updat(int root,int star,int end,int s1,int e1,int add) //只更新找的区间结点值,再加上个延迟标记 { if(s1>end||e1<star) return stu[root].sum; if(star>=s1&&end<=e1) { stu[root].abadd = add; stu[root].sum = (end-star+1)*add; return stu[root].sum; } down_updat(root,star,end); //传给左右子树延迟标记 int mid = (star+end)/2; int t1 = updat(2*root,star,mid,s1,e1,add); int t2 = updat(2*root+1,mid+1,end,s1,e1,add); //这不和查询一样,要更新区间值的; return stu[root].sum = t1 + t2; }
阅读全文
0 0
- 线段树?线段树!
- 线段树?线段树!
- 线段_线段树
- 线段_线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 线段树
- 关于C语言四种跳出语句
- pygame模块
- 莫烦机器学习笔记之小例子
- c++基础知识(命名空间、输入输出流、重载、缺省、引用)
- Lucene实践:全文检索的基本原理
- 线段树
- 获取数组长度
- 枚举例题,完美立方
- 关于IDEA启动Spring Boot项目出现Caused by: java.lang.NoClassDefFoundError: javax/servlet/ServletContext at j
- Android基础—Fragment
- 2017.10.11 第二天总结
- Python(3)Python35+Pyqt5安装
- 数据结构概念解析
- public、protected、private在C++和Java中的应用及区别