线段树 Myth丶恋晨 2022-05-29 22:16 334阅读 0赞 线段树入门: 转载博客:[点击打开链接][Link 1] 前几天开始接触线段树,其一些基本的操作还是很容易理解的,但是区间更新我着实理解了好一会(因该是本人太菜),今天有时间,所以总结一下。 这篇博客主要是讲一讲线段树的一些基本操作和我的一些理解。 首先我们需要知道线段树它是建立在线段的基础上的,树上的每个节点代表的是一条线段\[a,b\],并且树上每个点都可以维护该区间的某个性质,因此线段树在处理区间问题上是非常高效的。例如查找某个区间的最大值、最小值、求区间的和等,并且这几天发现线段树的题目都有个特点就是题目都会有多次查询,一般做这类问题我们可能会想到循环去找,时间复杂度为O(n),可是当数据范围很大并且查询次数很多的时候循环肯定达不到题目时间要求,但是用线段树可以很好的解决这类问题,时间复杂度仅为O(logN)。 先看看线段树长什么样子,本人手画,别嫌弃........ ![Center][] 从图中看出,树上每个节点有几个值,改点维护的区间的左端点、右端点、维护的性质、根据题目不同还会添加其他值例如需要给节点标记时要加上标记值等。一个节点有这么多性质因此我们通常用结构体来储存。如下(看个人习惯): \[cpp\] [view plain][] [copy][view plain] 1. const int Max\_n=10000; 2. 3. struct node 4. \{ 5. int l;//节点的左端点 6. int r;//节点的右端点 7. int val;//节点维护的该区间的某个性质 8. int tag;//给节点标记的变量 9. \}Segtree\[Max\_n\];//定义线段树 下面讲线段树最基本的建树,查询,单点更新,区间更新操作,用到比较多的是递归方法,直接看代码吧应该好懂: 建树操作: \[cpp\] [view plain][] [copy][view plain] 1. void pushup(int root) 2. \{ 3. segtree\[root\].val=max(segtree\[root<<1\].val,segtree\[root<<1|1\].val);//val维护的为区间最大值 4. \} 5. 6. //从图中不难看出节点的编号是从上往下从从左往右,因此从最顶层1号节点开始建树 7. void buildtree(int root,int l,int r)//root为当前结点,l为当前区间的左端点,r为当前区间的右端点 8. \{ 9. segtree\[root\].l=l;//当前节点维护区间的左端点为l,下同 10. segtree\[root\].r=r; 11. if(l==r)//找到最底层对的节点 12. \{ 13. segtree\[root\].val=1;//把节点的初始值都置为1 14. return;//找到底层元素后返回上一层 15. \} 16. int mid=(segtree\[root\].l+segtree\[root\].r)>>1;//mid为当前区间的中点。 17. //'>>1',该运算表示右移一位,在二进制状态下操作,相当于/2,'<<1',左移一位相当于\*2 18. buildtree(root<<1,l,mid);//左边建树,不难发现当前节点的左儿子编号为父亲切点的两倍 19. buildtree(root<<1|1,mid+1,r);//右边建树 20. pushup(root);//更新父亲节点 21. \} 查询操作: \[cpp\] [view plain][] [copy][view plain] 1. int query(int root,int l,int r)//root问哦查询节点,\[l,r\]为查询区间 2. \{ 3. int ll=segtree\[root\].l; 4. int rr=segtree\[root\].r; 5. if(l<=ll&&rr<=r)//如果该节点的区间刚好在查询区间内,则返回该节点的val 6. return segtree\[root\].val; 7. int mid=(ll+rr)>>1; 8. int ans=-inf;//ans为查询结果,初始化为一个很大的负值 9. if(l<=mid)//该节点的左边可查询 10. ans=max(ans,query(root<<1,l,r)); 11. if(r>mid)//右边可查询 12. ans=max(ans,query(root<<1|1,l,r)); 13. return ans; 14. \} 单点更新: \[cpp\] [view plain][] [copy][view plain] 1. void update(int root,int r,int cur)//当前节点,要更新的节点,要更新的的值 2. \{ 3. int ll=segtree\[root\].l; 4. int rr=segtree\[root\].r; 5. if(ll==rr)//找到最底层的点则更新 6. \{ 7. segtree\[root\].val=cur;//将该点的值改为cur; 8. return; 9. \} 10. int mid=(ll+rr)>>1; 11. if(r<=mid)//要更新的点在该区间的左边则去左边更新 12. update(root<<1,r,cur); 13. else //否则去右边更新 14. update(root<<1|1,r,cur); 15. pushup(root); 16. \} 区间更新: 区间更新如过把这个区间的所有的点都单点更新的的话并不能体现线段树优势,所以线段树是直接将整个区间维护的值进行修改,区间更新需要用到延迟更新(应该就这个位置不好理解一点),我个人理解延迟更新就是当你找到要更新的区间后对该区间维护的值进行修改然后给该节点打上标记,然后就 返回,不再继续向下更新。当下一次查询操作如果发现当前节点被标记过则把该节点的左右儿子更新并打上标记,这样原本在上一次操作就因该跟新的点延迟到了需要用到它的时候再更新,巧妙啊!!! \[cpp\] [view plain][] [copy][view plain] 1. void pushdown(int root) 2. \{ 3. if(segtree\[root\].tag)//如果这个点被标记过,则把他带到的左右儿子更新并打上标记 4. \{ 5. segtree\[root<<1\].val=max(segtree\[root\].tag,segtree\[root<<1\].val);//更新左儿子 6. 7. segtree\[root<<1|1\].val=max(segtree\[root\].tag,segtree\[root<<1|1\].val);//更新右儿子 8. segtree\[root<<1\].tag=segtree\[root<<1|1\].tag=segtree\[root\].tag;//标记 9. segtree\[root\].tag=0;//把该节点的标记还原,否则下次遇到还会更新 10. return ; 11. 12. \} 13. \} 14. void update(int root,int l,int r,int cur)//当前节点,更新的区间左右端点,更新值 15. \{ 16. int ll=segtree\[root\].l; 17. int rr=segtree\[root\].r; 18. if(l<=ll&&rr<r)//该节点的区间为要更新的区间,更新该节点并标记 19. \{ 20. segtree\[root\].val=max(cur,segtree\[root\].val); 21. segtree\[root\].tag=cur; 22. return; 23. \} 24. pushdown(root);//如果该节点被标记过则需要先更新他的儿子 25. int mid=(ll+rr)>>1; 26. if(l<=mid)//左边可更新 27. update(root<<1,l,r,cur); 28. if(l>mid)//右边可更新 29. update(root<<1|1,l,r,cur); 30. pushup(root); 31. \} 线段树的基本操作就就讲完了,我觉得只要仔细想想应该是很好懂的。 另外我们对线段树的操作一般都是从最顶端开始。 [Link 1]: http://blog.csdn.net/qq_37171272/article/details/74729210 [Center]: /images/20220530/965e07bfb8ba46f6b6d4faa20a21ce95.png [view plain]: http://blog.csdn.net/qq_37171272/article/details/74729210#
相关 线段树 线段树:创建时实际做的是后序遍历 > 1. 线段树不是完全二叉树 > 2. 线段树是平衡二叉树 > 3. 堆也是平衡二叉树,故完全二叉树是平衡二叉树,平衡二叉树不一 朱雀/ 2023年07月10日 12:39/ 0 赞/ 6 阅读
相关 线段树 [http://www.cnblogs.com/shuaiwhu/archive/2012/04/22/2464583.html][http_www.cnblogs.com_s 迈不过友情╰/ 2022年09月20日 09:13/ 0 赞/ 274 阅读
相关 线段树 1、概述 线段树,也叫区间树,是一个完全二叉树,它在各个节点保存一条线段(即“子数组”),因而常用于解决数列维护问题,它基本能保证每个操作的复杂度为O(lgN)。 2、线段 分手后的思念是犯贱/ 2022年08月10日 10:53/ 0 赞/ 230 阅读
相关 线段树 Ⅱ 单点修改(内容有升级) \[hdu2795\] ([http://acm.hdu.edu.cn/showproblem.php?pid=2795][http_acm.hdu 朴灿烈づ我的快乐病毒、/ 2022年08月03日 13:45/ 0 赞/ 289 阅读
相关 线段树 线段树:一棵完全二叉树,每个节点代表一个区间。 关于单点更新: \[hdu1166 \] ([http://acm.hdu.edu.cn/showproblem.php?p ﹏ヽ暗。殇╰゛Y/ 2022年08月03日 13:45/ 0 赞/ 281 阅读
相关 线段树 线段树入门: 转载博客:[点击打开链接][Link 1] 前几天开始接触线段树,其一些基本的操作还是很容易理解的,但是区间更新我着实理解了好一会(因该是本人太菜),今天有时 Myth丶恋晨/ 2022年05月29日 22:16/ 0 赞/ 335 阅读
相关 线段树 一 概述 线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(l 川长思鸟来/ 2022年05月17日 05:53/ 0 赞/ 316 阅读
相关 线段树 线段树(单点修改) 1 include <bits/stdc++.h> 2 using namespace std; 3 4 stru 亦凉/ 2021年12月03日 07:17/ 0 赞/ 388 阅读
相关 线段树 转载 [https://www.cnblogs.com/TheRoadToTheGold/p/6254255.html][https_www.cnblogs.com_TheRo 心已赠人/ 2021年07月16日 15:21/ 0 赞/ 525 阅读
还没有评论,来说两句吧...