Java数据结构之线段树中的懒操作详解
作者:chengqiuming 发布时间:2021-08-20 00:21:52
标签:Java,线段树,懒操作
一、问题提出
对于线段树,若要求对区间中的所有点都进行更新,可以引入懒操作。
懒操作包括区间更新和区间查询操作。
二、区间更新
对 [l,r] 区间进行更新,例如将 [l,r] 区间所有元素都更新为 v,步骤如下。
1.若当前节点区间被查询区间[l,r]覆盖,则仅对该节点进行更新并做懒标记,表示该节点已被更新,对该节点的子节点暂不更新。
2.判断是在左子树中查询还是在右子树中查询。在查询过程中,若当前节点带有懒标记,则将懒标记传给子节点(将当前节点的懒标记清除,将子节点更新并做懒标记),继续查找。
3.在返回时更新最值。
三、区间查询
带有懒标记的区间查询和普通的区间查询有所不同,在查询过程中若遇到节点有懒标记,则下传懒标记,继续查询。
四、实战
1.问题描述
2.输入
10
5 3 7 2 12 1 6 4 8 15
3 7 25
1 10
3.代码
package com.platform.modules.alg.alglib.p85;
public class P85 {
public String output = "";
private final int maxn = 100005;
private final int inf = 0x3f3f3f3f;
private int n;
private int a[] = new int[maxn];
private node tree[] = new node[maxn * 4]; // 树结点存储数组
public P85() {
for (int i = 0; i < tree.length; i++) {
tree[i] = new node();
}
}
void lazy(int k, int v) {
tree[k].mx = v; // 更新最值
tree[k].lz = v; // 做懒标记
}
// 向下传递懒标记
void pushdown(int k) {
lazy(2 * k, tree[k].lz); // 传递给左孩子
lazy(2 * k + 1, tree[k].lz); // 传递给右孩子
tree[k].lz = -1; // 清除自己的懒标记
}
// 创建线段树,k表示存储下标,区间[l,r]
void build(int k, int l, int r) {
tree[k].l = l;
tree[k].r = r;
tree[k].lz = -1; // 初始化懒操作
if (l == r) {
tree[k].mx = a[l];
return;
}
int mid, lc, rc;
mid = (l + r) / 2; // 划分点
lc = k * 2; // 左孩子存储下标
rc = k * 2 + 1; // 右孩子存储下标
build(lc, l, mid);
build(rc, mid + 1, r);
tree[k].mx = Math.max(tree[lc].mx, tree[rc].mx); // 结点的最大值等于左右孩子最值的最大值
}
// 将区间 [l,r] 修改更新为 v
void update(int k, int l, int r, int v) {
// 找到该区间
if (tree[k].l >= l && tree[k].r <= r) {
lazy(k, v); // 更新并做懒标记
return;
}
if (tree[k].lz != -1)
pushdown(k); // 懒标记下移
int mid, lc, rc;
mid = (tree[k].l + tree[k].r) / 2; // 划分点
lc = k * 2; // 左孩子存储下标
rc = k * 2 + 1; // 右孩子存储下标
if (l <= mid)
update(lc, l, r, v); // 到左子树更新
if (r > mid)
update(rc, l, r, v);// 到右子树更新
tree[k].mx = Math.max(tree[lc].mx, tree[rc].mx); // 返回时修改更新最值
}
// 求区间 [l,r] 的最值
int query(int k, int l, int r) {
int Max = -inf;
if (tree[k].l >= l && tree[k].r <= r) // 找到该区间
return tree[k].mx;
if (tree[k].lz != -1)
pushdown(k); // 懒标记下移
int mid, lc, rc;
mid = (tree[k].l + tree[k].r) / 2; // 划分点
lc = k * 2; // 左孩子存储下标
rc = k * 2 + 1; // 右孩子存储下标
if (l <= mid)
Max = Math.max(Max, query(lc, l, r)); // 到左子树查询
if (r > mid)
Max = Math.max(Max, query(rc, l, r)); // 到右子树查询
return Max;
}
public String cal(String input) {
int l, r;
int i, v;
String[] line = input.split("\n");
n = Integer.parseInt(line[0]); // 第 1 行:10
String[] nums = line[1].split(" ");
// 第 2 行:5 3 7 2 12 1 6 4 8 15
for (i = 1; i <= n; i++)
a[i] = Integer.parseInt(nums[i - 1]);
build(1, 1, n); // 创建线段树
// 输入查询最值的区间 [l,r] 1 10
String[] range = line[2].split(" ");
l = Integer.parseInt(range[0]);
r = Integer.parseInt(range[1]);
// 求区间[l..r]的最值
output += query(1, l, r) + "\n";
// 输入更新的区间值 l r v 第 3 行: 3 7 25
String[] change = line[3].split(" ");
l = Integer.parseInt(change[0]);
r = Integer.parseInt(change[1]);
v = Integer.parseInt(change[2]);
// 将区间 [l,r] 修改更新为 v
update(1, l, r, v);
// 求区间[l..r]的最值 第 4 行:1 10
range = line[4].split(" ");
l = Integer.parseInt(range[0]);
r = Integer.parseInt(range[1]);
// 求区间 [l..r] 的最值
output += query(1, l, r) + "\n";
return output;
}
}
// 结点
class node {
int l; // l 表示区间左右端点
int r; // r 表示区间左右端点
int mx; // mx表示区间[l,r]的最值
int lz; // lz 懒标记
}
4.测试
来源:https://blog.csdn.net/chengqiuming/article/details/127150347


猜你喜欢
- 前言:来这家公司上班后,开始使用Git作为项目版本控制系统,由于以前用的是SVN,所以对Git也就简单学习了一下。但是,实践出真知,当开始使
- 1.sonarQube的简介SonarQube是一款自动化代码审查工具,用于检测代码中的错误、漏洞和代码异味。它可以与你现有的工作流集成,以
- 约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;
- 本文实例为大家分享了android实现选项卡功能,通过计算偏移量,设置tetxview和imageView的对应值,一些color的值读者自
- 在Android开发中,有时我们需要对SQLite数据库进行增,删,查,找等操作,现在就来简单介绍一下,以下为详细代码。 &nbs
- 本文实例讲述了Android之复选框对话框用法。分享给大家供大家参考。具体如下:main.xml布局文件<?xml version=&
- 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing)。内存映射文件对于托管世界的开发人
- 这篇文章主要介绍了spring boot如何配置请求的入参和出参json数据格式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一
- 一. * 搭建及配置1 . * 简介 * 是架设在局域网的一种特殊的远程仓库,目的是代理远程仓库及部署第三方构件。有了 * 之后,当 Maven
- 这个是SpringBoot的Maven插件,主要用来打包的,通常打包成jar或者war文件。其中goal标签可以有5个值:repackage
- spring:1)开源框架2)IoC(控制反转),将类的创建和依赖关系写在配置文件里,由配置文件注入,实现了松耦合3)AOP 将安全,事务等
- 包装类包装类其实就是8种基本数据类型对应的引用类型。基本数据类型引用数据类型byteByteshortShortintIntegerlong
- 前言我们一说到spring,可能第一个想到的是 IOC(控制反转) 和 AOP(面向切面编程)。没错,它们是spring的基石,得益于它们的
- 举例说明:1、有一个200*200像素的窗口,想要把它放在800*600像素的屏幕中间,屏幕的位置应是(800/2,600/2)=(400,
- 这篇文章主要介绍了java多线程关键字final和static详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价
- java中实现list或set转map的方法在开发中我们有时需要将list或set转换为map(比如对象属性中的唯一键作为map的key,对
- 什么是WebSocket?WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信—
- springboot+mybatis整合过程中,开启控制台sql语句打印的多种方式:方法一1>(spring+mybatis)在myb
- c# Invoke与BeginInvoke最近在学习线程时,发现当我创建的线程需要访问UI界面的时,会发生异常,原因是我在跨线程调用主线程的
- 接触Spring快半年了,前段时间刚用Spring4+S2H4做完了自己的毕设,但是很明显感觉对Spring尤其是IOC容器的实现原理理解的