Java数据结构之线段树中的懒操作详解

 更新时间:2022年10月04日 08:16:46   作者:chengqiuming  
对于线段树,若要求对区间中的所有点都进行更新,可以引入懒操作。懒操作包括区间更新和区间查询操作。本文将通过一个示例和大家详细聊聊线段树中的懒操作,需要的可以参考一下

一、问题提出

对于线段树,若要求对区间中的所有点都进行更新,可以引入懒操作。

懒操作包括区间更新和区间查询操作。

二、区间更新

对 [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.测试

以上就是Java数据结构之线段树中的懒操作详解的详细内容,更多关于Java线段树 懒操作的资料请关注脚本之家其它相关文章!

相关文章

  • SpringBoot整合Druid数据库连接池的方法

    SpringBoot整合Druid数据库连接池的方法

    Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。这篇文章主要介绍了SpringBoot整合Druid数据库连接池的方法,需要的朋友可以参考下
    2020-07-07
  • SpringBoot项目实战之数据交互篇

    SpringBoot项目实战之数据交互篇

    这篇文章主要给大家介绍了关于SpringBoot项目实战之数据交互篇的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-03-03
  • pagehelper踩坑记之分页乱套问题解决

    pagehelper踩坑记之分页乱套问题解决

    这篇文章主要为大家介绍了pagehelper踩坑记之分页乱套问题解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • java链表的常见简单面试算法题详解

    java链表的常见简单面试算法题详解

    文章总结:本文主要介绍了单链表的基本操作,包括头插法、尾插法、链表翻转、链表成环判断、成环位置判断、成环长度判断,以及有序链表的合并,通过实例和代码示例,详细讲解了每种操作的原理和实现方法
    2025-01-01
  • Springboot中如何自动转JSON输出

    Springboot中如何自动转JSON输出

    这篇文章主要介绍了Springboot中如何自动转JSON输出,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • IntelliJ IDEA 设置数据库连接全局共享的步骤

    IntelliJ IDEA 设置数据库连接全局共享的步骤

    在日常的软件开发工作中,我们经常会遇到需要在多个项目之间共享同一个数据库连接的情况,默认情况下,IntelliJ IDEA 中的数据库连接配置是针对每个项目单独存储的,幸运的是,IntelliJ IDEA 提供了一种方法来将数据库连接配置设置为全局共享,从而简化这一过程
    2024-10-10
  • java向数据库插入数据显示乱码的几种问题解决

    java向数据库插入数据显示乱码的几种问题解决

    这篇文章主要给大家介绍了关于java向数据库插入数据显示乱码问题的解决方案,文章分别罗列了前台乱码的问题、前台先后台插入数据后台接收到的数据是乱码以及后台向数据库插入数据是乱码等几种情况,需要的朋友可以参考下
    2021-11-11
  • SpringMVC后端Controller页面跳转的三种方式汇总

    SpringMVC后端Controller页面跳转的三种方式汇总

    这篇文章主要介绍了SpringMVC后端Controller页面跳转的三种方式汇总,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • Java用Arrays.asList初始化ArrayList实例方法

    Java用Arrays.asList初始化ArrayList实例方法

    在本篇文章里小编给大家分享的是关于Java中使用Arrays.asList初始化ArrayList的知识点内容,需要的朋友们参考下。
    2019-10-10
  • 如何通过idea实现springboot集成mybatis

    如何通过idea实现springboot集成mybatis

    这篇文章主要介绍了如何通过idea实现springboot集成mybatis,使用springboot 集成 mybatis后,通过http请求接口,使得通过http请求可以直接操作数据库,本文结合实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-09-09

最新评论