背景:
在上一篇线段树入门中涉及到对线段树的更新操作是单点操作,即更新的是一个,如果现在把问题升级下,让你将数组nums的某个区间[start,end]的值都加上value,该怎么做好呢?比较容易想到的是可以复用之前的更新操作,外面给它套个for循环,依次更新,这样虽然结果正确,但时间复杂度不堪入目…很明显是不可取的,基于这个问题,从而有了lazy思想,下面开始介绍。

概述:
lazy,顾名思义,就是懒惰的意思,在本问题中所体现的则是在进行区间更新时,延迟更新,用一个lazy标记记录下就好,没必要依次更新它的子节点,等到正真用到了的时候,为保证结果的准确性,需要将lazy标记传递下去即执行下pushDown操作,这样便能大大提高了效率。

实现:
首先定义了一个TreeNode类,用于封装一些信息,如下,

private static class TreeNode {int left, right;int sum, lazy;public TreeNode(int left, int right) {this.left = left;this.right = right;}public int mid() {return left + (right - left) / 2;}public int length() {return right - left + 1;}}

并且,在该类中还定义了取该节点区间中点的方法和取该节点区间的长度的方法,这里把它定义为静态内部类,静态内部类与成员内部类的区别是:静态内部类在实例化的时候不需要有外部类的对象来实例化,而成员内部类实例化的时候是需要的。

接着就是线段树的构造,

public void build(int index, int start, int end, int[] nums) {tree[index] = new TreeNode(start, end);if (start == end) {tree[index].sum = nums[start];return;}int mid = tree[index].mid();build(2 * index + 1, start, mid, nums);build(2 * index + 2, mid + 1, end, nums);pushUp(index);}

与之前的差别不大,这里的pushUp方法如下,

public void pushUp(int index) {tree[index].sum = tree[2 * index + 1].sum + tree[2 * index + 2].sum;}

其实就是更新和sum,

然后来看下pushDown方法,

public void pushDown(int index) {if (tree[index].lazy != 0) {int lazy = tree[index].lazy;int left = 2 * index + 1;int right = 2 * index + 2;tree[left].lazy += lazy;tree[right].lazy += lazy;tree[left].sum += lazy * tree[left].length();tree[right].sum += lazy * tree[right].length();tree[index].lazy = 0;}}

该方法的语义是,将当前节点的lazy标记向子节点传,如果lazy不为0,即存在标记,就往下更新,更新完之后当前节点的lazy标记需要复原,即重新置为0。

更新操作,

public void update(int index, int start, int end, int value) {if (tree[index].left == start && tree[index].right == end) {tree[index].lazy += value;tree[index].sum += value * (tree[index].length());return; //当前节点区间吻合时,更新该节点,其子节点就不急着更新了,直接返回}if (tree[index].left == tree[index].right) {return;}// 每次要更新之前,我先pushDown下,保证结果的正确性pushDown(index);int mid = tree[index].mid(), left = 2 * index + 1, right = 2 * index + 2;if (mid >= end) {update(left, start, end, value);} else if (mid < start) {update(right, start, end, value);} else {update(left, start, mid, value);update(right, mid + 1, end, value);}pushUp(index);}

查询方法,

public int search(int index, int start, int end) {if (tree[index].left == start && tree[index].right == end) {return tree[index].sum;}// 同样的,每次查询之前,看看当前节点的lazy是否标记过pushDown(index);int mid = tree[index].mid(), left = 2 * index + 1, right = 2 * index + 2;int sum = 0;if (mid >= end) {sum += search(left, start, end);} else if (mid < start) {sum += search(right, start, end);} else {sum += search(left, start, mid);sum += search(right, mid + 1, end);}return sum;}

上面的更新与查询操作分支有点多,其实也可以再小小的优化下。

优化:
优化后的更新操作,

public void update1(int index, int start, int end, int value) {if (tree[index].left >= start && tree[index].right <= end) {tree[index].lazy += value;tree[index].sum += value * tree[index].length();return;}pushDown(index);int mid = tree[index].mid();if (mid >= start) {update1(2 * index + 1, start, end, value);}if (mid < end) {update1(2 * index + 2, start, end, value);}pushUp(index);}

优化后的查询操作,

public int search1(int index, int start, int end) {if (tree[index].left >= start && tree[index].right <= end) {return tree[index].sum;}pushDown(index);int sum = 0, mid = tree[index].mid();if (mid >= start) {sum += search1(2 * index + 1, start, end);}if (mid < end) {sum += search1(2 * index + 2, start, end);}return sum;}

读者可以自己思考下,这两种实现方式为什么是等价的,这样有助于更深入的掌握线段树的实现。

最后完整代码如下,

public class SegmentTree {TreeNode[] tree;private static class TreeNode {int left, right;int sum, lazy;public TreeNode(int left, int right) {this.left = left;this.right = right;}public int mid() {return left + (right - left) / 2;}public int length() {return right - left + 1;}}public SegmentTree(int[] nums) {tree = new TreeNode[4 * nums.length];build(0, 0, nums.length - 1, nums);}public void build(int index, int start, int end, int[] nums) {tree[index] = new TreeNode(start, end);if (start == end) {tree[index].sum = nums[start];return;}int mid = tree[index].mid();build(2 * index + 1, start, mid, nums);build(2 * index + 2, mid + 1, end, nums);pushUp(index);}public int search(int index, int start, int end) {if (tree[index].left == start && tree[index].right == end) {return tree[index].sum;}pushDown(index);int mid = tree[index].mid(), left = 2 * index + 1, right = 2 * index + 2;int sum = 0;if (mid >= end) {sum += search(left, start, end);} else if (mid < start) {sum += search(right, start, end);} else {sum += search(left, start, mid);sum += search(right, mid + 1, end);}return sum;}public int search1(int index, int start, int end) {if (tree[index].left >= start && tree[index].right <= end) {return tree[index].sum;}pushDown(index);int sum = 0, mid = tree[index].mid();if (mid >= start) {sum += search1(2 * index + 1, start, end);}if (mid < end) {sum += search1(2 * index + 2, start, end);}return sum;}public void update(int index, int start, int end, int value) {if (tree[index].left == start && tree[index].right == end) {tree[index].lazy += value;tree[index].sum += value * (tree[index].length());return;}if (tree[index].left == tree[index].right) {return;}pushDown(index);int mid = tree[index].mid(), left = 2 * index + 1, right = 2 * index + 2;if (mid >= end) {update(left, start, end, value);} else if (mid < start) {update(right, start, end, value);} else {update(left, start, mid, value);update(right, mid + 1, end, value);}pushUp(index);}public void update1(int index, int start, int end, int value) {if (tree[index].left >= start && tree[index].right <= end) {tree[index].lazy += value;tree[index].sum += value * tree[index].length();return;}pushDown(index);int mid = tree[index].mid();if (mid >= start) {update1(2 * index + 1, start, end, value);}if (mid < end) {update1(2 * index + 2, start, end, value);}pushUp(index);}public void pushDown(int index) {if (tree[index].lazy != 0) {int lazy = tree[index].lazy;int left = 2 * index + 1;int right = 2 * index + 2;tree[left].lazy += lazy;tree[right].lazy += lazy;tree[left].sum += lazy * tree[left].length();tree[right].sum += lazy * tree[right].length();tree[index].lazy = 0;}}public void pushUp(int index) {tree[index].sum = tree[2 * index + 1].sum + tree[2 * index + 2].sum;}
}

线段树进阶之lazy思想及Java实现相关推荐

  1. hdu 5124(线段树区间更新+lazy思想)

    http://acm.hdu.edu.cn/showproblem.php?pid=5124 题意:区间覆盖次数问题. 解题思路:线段树水之. #include<iostream> #in ...

  2. 51nod3077 线段树进阶1

    3077 线段树进阶1 给出一个长度为n的整数序列a1,a2,-,an,进行m次操作,操作分为两类. 操作1 ​:给出l,r,v,将al,al+1,-,ar分别加上v. 操作2 ​:给出l,r,询问 ...

  3. 【总结】线段树 进阶

    我竟然开了这么多坑...ε=(´ο`*)))唉,慢慢填吧... 线段树 进阶 前置芝士 然后我们来讲一个好玩的东西,叫权值线段树. 权值线段树&动态开点 有一个数列,数列里的每个不同的 aia ...

  4. 数据结构之线段树进阶(区间更新lazy标记)

    之前说了线段树的点更新和区间求和.其实点更新是区间更新的一种最基础的做法.我们把一个点想像成一个区间的话,不就是最简单的区间更新了嘛. 为什么要把区间更新和点更新分开来看呢?假如我们对区间[l,r]进 ...

  5. 洛谷2023-维护序列-线段树-两个lazy的相互作用

    题目描述: 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的 ...

  6. YJJ's Salesman HDU - 6447(线段树 单点更新+DP思想)

    YJJ's Salesman 题目链接:HDU - 6447 题意:一个1e9*1e9的地图,要求由(0, 0) -> (1e9, 1e9):只能向下,向右, 向右下移动:地图中有n个点,有宝藏 ...

  7. 线段树进阶之模板观见

    ----------------------已识乾坤大,犹怜草木青. 先介绍一篇优秀的洛谷博文:https://www.luogu.org/problemnew/solution/P3372 ---- ...

  8. 浅谈数据结构之主席树(线段树进阶版)

    今天看了点主席树的概念,加上飞哥上次讲的,目前对主席树有了大致的了解,简单谈谈吧,不讲代码,只讲思路,日后贴题! Orz高级数据结构发明者主席!!最早在CLJ的课件里第一次看到了这个词,最近做区间第K ...

  9. 线段树进阶(懒惰标记)

    对于朴素线段树,要进行区间更新,如果按照单点更新的方法更新,他的复杂度很高,比数组暴力更新还要慢. 所以我们使用懒惰标记,他走到包含区间就不往下走了,然后就更新区间值,并打上懒惰标记.那走到这不走了, ...

  10. 线段树进阶之清风拂面

    --------------------------山前停马蹄,绿风揉春水,林间耸翠如眼,崖前无尽深渊.行到星子入夜,风声轻拂,恣意大观. 一个问题,只要能转化成对一些连续点的修改和统计问题,基本就可 ...

最新文章

  1. Dancing Link讲解
  2. 在主窗体中打开一个新子窗体,如果已有子窗体,则激活它,而不打开新的。...
  3. 一个可以支持多版本的MediaPlayer的控件做法(支持MediaPlayer6,7,8,9,10的播放)
  4. 解决报错:java.lang.NoSuchMethodException: com.tangyuan.entity.RicherProduct.<init>()
  5. jvm内存模型_JVM内存模型的相关概念
  6. 作者:​林旺群(1983-),男,博士,北京系统工程研究所助理研究员。
  7. Python三维绘图--Matplotlib colorbar生成
  8. SilverLight明日起通过微软更新推送
  9. 十年测开如何理解自动化测试里的数据驱动、关键字驱动思路
  10. 分析 js构造函数:对象方法 、类方法 、原型方法
  11. python zip用法_python zip用法
  12. python设置ini文件中的值_5分钟掌握Python中常见的配置文件
  13. 关于Firefox插件
  14. 最长公共子序列(输出公共序列)
  15. 在线java面试题库_Java笔试题库
  16. C# TextBox 自动换行问题解决
  17. Redis由于目标计算机积极拒绝,无法连接。
  18. 数据流通利用 | 数据产权研究综述
  19. IE火狐的代理服务器的设置
  20. 清理数据 python_使用python的数据清理技术

热门文章

  1. AI上推荐 之 MIND(动态路由与胶囊网络的奇光异彩)
  2. 图片怎么转文字?建议收藏这些方法
  3. 主机域名中什么叫计算机名,主机域名中的主机名是什么概念
  4. 利用ADO连接数据库时,Rs.recordcount总是返回-1,解决办法
  5. oracle 返回 xml解析,Oracle xmltable解析返回LPX-00209(Oracle xmltable parsing return LPX-00209)...
  6. 无法安装冰点还原_冰点还原标准版v8.56.020.5542 ——墨涩网
  7. log2 3怎样用计算机打出,红警在局域网怎么样才可以2个人打多个电脑玩家?要打3个电脑以上的...
  8. 游戏程序员如何正确的写简历
  9. 【视频教程】帝国CMS模板开发制作网站系列教程04
  10. 在文档类中控制舞台上影片剪辑