线段树之延迟数组_20230410

news2024/12/27 13:48:55

线段树 之 延迟数组

  1. 前言

线段树是一类特殊的完全二叉树,其本质是对递归过程信息的再处理和再记忆,线段树构造完成后,可以脱离原始数组或线性表,直接对线段树进行查询或更新操作即可。线段树的储存结构和堆结构类似,如果父节点的下标为v,那么左孩子节点的下标可以表示为2*v, 右孩子节点的下标可以表示为2*v+1,依此类推。

  1. 延迟数组定义

延迟数组采取的是空间换取时间的策略,在递归遍历线段树过程中,在某些时候只需要对局部区间进行相关更新/查询,而不需要深入到底层进行更新/查询,在后续需要的时候再继续更新操作。那么这就带来一个问题,后续操作中,如何判断是否需要额外更新呢? 这就需要定义延迟数组,延迟数组可以选择布尔类型,也可以选择其它数据类型,它的作用是在后续的更新或查询操作时候,把信息提前传递给双子节点,在需要采集信息之前,对双子节点的信息进行相关的更新/查询。

延迟数组可以降低递归的深度,提前结束递归,比如我们游览大型公园,对于有些景点,由于时间关系,不能深入景点参观,可以在入口处标记“到此一游,下面景点需要门票5元”,藉此提醒,下次再踏入后续景点前,直接买好门票游览即可。

延迟数组在更新线段树的时候,如果查询到某个区间满足更新要求,那么它直接更新此区间的值,同时对此区间上的延迟数组的值进行相应的更新即可,而无需更新其子孩子。

  1. 延迟数组应用

首先我们建立一个区间最大值的线段树,然后更新操作对区间[l,r]范围内的所有元素加上某个常数addend, 更新操作完成后, 再查询某个区间内的最大值。

用一个具体例子来说明,已知数组a[0…4]={1, 3, 16, 8, 6}, 根据数组建立区间最大值线段树,线段树为二叉树结构,表示为,

在这里插入图片描述

要求在区间a[2…4]所有的元素都加上10,并定义lazy[]数组跟踪相关情况。在更新过程中,由于有lazy数组进行标记,

在这里插入图片描述

整个过程中,只需要更新a[2…2], a[0…2], a[3…4]和a[0…4],注意观察,过程中并没有更新a[3…3]和a[4…4],这一步就是lazy[]数组发挥作用的地方,如果二叉树深度很大,那么就可以节省大量的遍历时间。

接下来我们回到查询最大值的过程,假设要求查询a[4…4]区间上的最大值,在查询过程中,遍历过程可以表示为,

在这里插入图片描述

先遍历a[0…2]返回-∞,然后来到a[3…4],此时需要调用push函数,把父节点的lazy数组当中的信息和操作传递给子节点,操作之后a[3…3]更新为18,a[4…4]更新为16;由于a[3…3]不满足边界条件,所以返回-∞,a[4…4]返回16(更新后的值),然后回退,比较max(-∞,16)得到16,然后再比较max(-∞,16)得到最终的返回值16,也就是a[4…4]区间上的最大值。

  1. 代码实现

建立线段树的实现,之前文章已经阐述,终点关注3个函数push函数,update函数和更新后的query函数。

push函数的作用是在更新和查询之前,把父节点里面的信息/指令操作传递给子节点,并在子节点上进行相关的操作,操作完成后,父节点的lazy数组复原。

update函数的道理也类似,在进行递归之前,需要先调用push函数,对子节点的信息进行更新,然后再往下进行。

对于 query函数,道理相同。

第一部分,头文件定义:

/**
 * @file Lazy_query.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-04-10
 * 
 * @copyright Copyright (c) 2023
 * 
 */

#ifndef LAZY_QUERY_H
#define LAZY_QUERY_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#define MAXN 10

int t[4*MAXN]; // t will be segment tree array

int lazy[4*MAXN]={0};

/**
 * @brief Build segment tree based on segment array and original array
 * 
 * @param nums Original array to provide the number
 * @param v Segement array index(2*v, 2*v+1)
 * @param tl Total left boundary
 * @param tr  Total right boundary
 */
void build(int *nums,int v,int tl, int tr);




void push(int v);

int query_lazy(int v, int tl, int tr, int l, int r);


/**
 * @brief Add addend to the range from l to r
 * 
 * @param v Segement tree index
 * @param tl Total left boundary
 * @param tr Total right boundary
 * @param l  To be searched left side
 * @param r  To be searched right side
 * @param addend To be added with the number of addend
 */
void update_lazy(int v, int tl, int tr, int l, int r, int addend);


int min(int a, int b);


int max(int a, int b);

#endif

第二部分,基本函数的实现,

/**
 * @file lazy_query.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-04-10
 * 
 * @copyright Copyright (c) 2023
 * 
 */

#ifndef LAZY_QUERY_C
#define LAZY_QUERY_C
#include "lazy_query.h"

void build(int *nums, int v, int tl, int tr)
{
    int tm;

    if(tl==tr)
    {
        t[v]=nums[tl]; //exit entry for the recursion
    }
    else
    {
        tm=(tl+tr)/2; //divide the segement t[] into two parts
        build(nums,2*v,tl,tm); //2*v represents the left part of the segment tree
        build(nums,2*v+1,tm+1,tr); //2*v+1 represents the right part of the segment tree
        t[v]=max(t[2*v],t[2*v+1]); //post-order traversal, 左右中,左边值和右边值已经求出
    }

    return;
}


void push(int v)
{
    t[2*v]+=lazy[v];
    t[2*v+1]+=lazy[v];// add lazy[v] to the next level

    lazy[2*v]+=lazy[v];
    lazy[2*v+1]+=lazy[v]; //pass down to the next level

    lazy[v]=0; //return to original point
}

int query_lazy(int v, int tl, int tr, int l, int r)
{
    int tm;
    int left_side;
    int right_side;
    int max_value;

    if(l>r)
    {
        return INT_MIN;
    }
    else if(tl==l && tr==r)
    {
        return t[v];
    }
    else
    {
        push(v); // update its decedent children
        tm=(tl+tr)/2;

        left_side=query_lazy(2*v,tl,tm,l,min(r,tm));
        right_side=query_lazy(2*v+1,tm+1,tr,max(l,tm+1),r);

        max_value=max(left_side,right_side);

        return max_value;
    }
}

void update_lazy(int v, int tl, int tr, int l, int r, int addend)
{
    int tm;
    if(l>r)
    {
        return; // no action with any value updated
    }

    if(tl==l && tr==r)
    {
        t[v]=t[v]+addend;
        lazy[v]+=addend; // in the range, lazy[v] hadd been updated
    }
    else
    {
        push(v);
        tm=(tl+tr)/2;

        update_lazy(2*v,tl,tm,l,min(r,tm),addend);
        update_lazy(2*v+1,tm+1,tr,max(l,tm+1),r,addend);

        t[v]=max(t[2*v],t[2*v+1]);
    }

    return;

}

int min(int a, int b)
{
    return (a<b?a:b);
}

int max(int a, int b)
{
    return(a>b?a:b);
}

#endif

第三部分,测试函数

/**
 * @file lazy_query_main.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-04-10
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef LAZY_QUERY_MAIN_C
#define LAZY_QUERY_MAIN_C
#include "lazy_query.c"

int main(void)
{
    int nums[]={1, 3, 16, 8, 6};
    int nums_size=sizeof(nums)/sizeof(int);

    int tl;
    int tr;
    int l;
    int r;
    int pos;
    int max_value;
    int addend;

    tl=0;
    tr=nums_size-1;
    l=2;
    r=4;
    addend=10;

    pos=1;
   

    build(nums,1,tl,tr);
    update_lazy(1,tl,tr,l,r,addend);
    max_value=query_lazy(1,tl,tr,4,4);

    getchar();

    return EXIT_SUCCESS;
}


#endif

  1. 小结

本文对延迟数组的应用做了简单分析,延迟数组核心思想是“60分万岁,一分不多,一分不少“,每一步都严格按照需求做到最小化的更新或查询,尽可能减少更新或查询的时间,做到程序的时间复杂度最低;代价是需要定义一个和线段树同维度的lazy数组,对每一步的信息进行记录,以便后续把父节点的信息及时传递给子节点。

参考资料:

Segment Tree - Algorithms for Competitive Programming (cp-algorithms.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/422170.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot——集成elasticsearch进行搜索并高亮关键词

目录 1.elasticsearch概述 3.springboot集成elasticsearch 4.实现搜索并高亮关键词 1.elasticsearch概述 &#xff08;1&#xff09;是什么&#xff1a; Elasticsearch 是位于 Elastic Stack 核心的分布式搜索和分析引擎。 Lucene 可以被认为是迄今为止最先进、性能最好的…

软测界的黑科技,难道不来瞧瞧?

写在前面&#xff1a; 在当今互联网时代&#xff0c;软件已经渗透到了人们生活的方方面面&#xff0c;各种类型的软件应运而生&#xff0c;为人们的工作和生活提供了更便捷的服务。然而&#xff0c;随着软件的不断增长和复杂性的不断提高&#xff0c;软件测试变得越来越重要。…

字节跳动AI-LAB | 算法三轮技术面分享

作者 | 太蔡了整理 | NewBeeNLP面试锦囊之面经分享系列&#xff0c;持续更新中 可以后台回复"面试"加入交流讨论组噢写在前面楼主是C9末流渣硕一枚&#xff0c;现在已经正式确定offer要去我宇宙条了&#xff01;当时为了准备面试几乎把网上头条的面经翻了个底朝天&am…

php(phar)反序列化漏洞及各种绕过姿势

概念&#xff1a; 序列化其实就是将数据转化成一种可逆的数据结构&#xff0c;自然&#xff0c;逆向的过程就叫做反序列化。简单来说就是我在一个地方构造了一个类&#xff0c;但我要在另一个地方去使用它&#xff0c;那怎么传过去呢&#xff1f;于是就想到了序列化这种东西&a…

句柄泄露的分析思路

基础知识 问题 什么是句柄&#xff1f; 句柄(file descriptor)即文件描述符&#xff0c;简称fd。Linux 系统中&#xff0c;把一切设备都视作文件&#xff0c;当进程打开现有文件或创建新文件时&#xff0c;内核向进程返回一个文件描述符。 FD作为文件句柄的实例&#xff0c;…

Nacos安装配置

一、下载Nacos Server。 通过Release页面进行下载&#xff0c;这里我以windows版本为例。 二、修改Nacos Server配置文件。 下载完成后&#xff0c;解压安装包后得到如下文件夹 为了配置集群&#xff0c;将nacos-server复制相同的两份&#xff0c;分别为命名为nacos-clust…

Java中有了equals(),为什么还有“==“

背景&#xff1a;Java中“一切皆是对象”&#xff0c;为什么还有非对象的“”&#xff1f; 在Java语言假设我们只进行OOP&#xff0c;所以Java代码都是由一个接着一个的类组成的。那么&#xff0c;对象之间比较&#xff0c;用equals()就可以了。 可为什么“”在代码随处可见呢…

海尔智家业绩尚可,但其智能家居“全场景”没做起来

‍数据智能产业创新服务媒体——聚焦数智 改变商业近日&#xff0c;海尔智家发布了其2022年年报&#xff0c;数据显示&#xff0c;2022年海尔智家实现收入2435.14亿元&#xff0c;同比增长7.2%&#xff1b;扣非归母净利润139.63亿元&#xff0c;同比增长18%&#xff0c;利润增…

消息队列kafka及zookeeper机制

目录 一、zookeeper 1、zookeeper简介 2、zookeeper特点 3、zookeeper工作模式及机制 4、zookeeper应用场景及选举机制 5、zookeeper集群部署 ①实验环境 ②安装zookeeper 二、消息队列kafka 1、为什么要有消息队列 2、使用消息队列的好处 3、kafka简介 4、kafka…

多模态模型学习1——CLIP对比学习 语言-图像预训练模型

多模态模型学习1——CLIP对比学习 语言-图像预训练模型学习前言什么是CLIP模型代码下载CLIP实现思路一、网络结构介绍1、Image Encodera、PatchPosition Embeddingb、Transformer EncoderI、Self-attention结构解析II、Self-attention的矩阵运算III、MultiHead多头注意力机制IV…

maya arnold自定义aov分层灯光组添加方法

一、问题描述&#xff1a; maya的arnold aov层灯光组(Light groups)运用有两种方法&#xff0c;总结一下使用笔记。灯光效果如下图&#xff1a; 二、制作过程&#xff1a; 1、灯光的布局主要用了两盏区域光&#xff0c;主光和辅助光。如下图&#xff1a; 2、主光为白色&am…

windows10下VS2019编译jpegsrc.v9e.tar.gz为lib静态库(已验证)

一、下载jpegsrc 1、下载JPEG库的源代码 http://www.ijg.org/files/ 2、这里使用最新的jpegsr9e.zip &#xff0c;别下载错误了&#xff08;jpegsrc.v9e.tar.gz 是RAM架构的仅支持32位&#xff09; 3、解压jpegsr9e.zip到d盘&#xff0c;如&#xff1a;D:\jpeg-9e 二、vs2019…

【Blender 水墨材质】实现过程简单剖析

写在前面 想把Blender一位大佬演示的Blender水墨材质过程&#xff0c;在Unity用Shader重现&#xff0c;过程中会拿能拿到的节点代码举例&#xff08;ShaderGraph或者UE的都会有&#xff09;。第一步当然是要跟着人家做一遍&#xff01;我会尽可能地分析一下每一步的原理~ 教程…

【数据挖掘与商务智能决策】第十二章 XGBoost算法和LightGBM算法

12.1.3 XGBoost算法的简单代码实现 XGBoost模型既可以做分类分析&#xff0c;也可以做回归分析&#xff0c;分别对应的模型为XGBoost分类模型&#xff08;XGBClassifier&#xff09;及XGBoost回归模型&#xff08;XGBRegressor&#xff09;。 XGBoost模型的安装办法可以采用P…

Linux--进程信号

前言 无人问津也好&#xff0c;技不如人也罢&#xff0c;你都要试着安静下来&#xff0c;去做自己该做的事情&#xff0c;而不是让烦恼和焦虑毁掉你不就不多的热情和定力。心可以碎&#xff0c;手不能停&#xff0c;该干什么干什么&#xff0c;在崩溃中继续努力前行&#xff0c…

MyBatis整合Springboot多数据源实现

前言 数据源&#xff0c;实际就是数据库连接池&#xff0c;负责管理数据库连接&#xff0c;在Springboot中&#xff0c;数据源通常以一个bean的形式存在于IOC容器中&#xff0c;也就是我们可以通过依赖注入的方式拿到数据源&#xff0c;然后再从数据源中获取数据库连接。 那么…

easyrecovery2023电脑文件数据恢复软件功能介绍

EasyRecovery功能全面&#xff0c;即便是没有经验的小白用户也可以很快上手&#xff0c;让你足不出户即可搞定常见的数据丢失问题。 在使用和操作存储设备期间&#xff0c;数据丢失问题在所难免。比如&#xff0c;误删除某个文件、不小心将有数据的分区格式化、误清空了有重要…

【ZUUL2踩坑】题一:Ribbon集成动态properties存在的原生风险

目录 一、问题背景 二、问题分析 1、配置文件空档期的问题 一、问题背景 JAVA的Properties工具有两种写配置文件的方式&#xff0c;一种是覆盖&#xff0c;一种是追加。 但是动态配置文件一般需要进行创建或更新&#xff0c;不会选择追加内容&#xff0c;所以只能选择进行配…

你的 Kubernetes 安全吗?最新benchmark的重要趋势解读

导语 疫情过后经济处在缓慢复苏的阶段&#xff0c;对于企业应该优先考虑数字化转型&#xff0c;因为它可以促进增长和创新。 不可避免地&#xff0c;当今的数字化转型计划依赖于云的可扩展性和灵活性。 虽然在云中启动应用程序和服务带来了许多机遇&#xff0c;但也带来了新的…

函数栈帧的创建与销毁

魔王的介绍&#xff1a;&#x1f636;‍&#x1f32b;️一名双非本科大一小白。魔王的目标&#xff1a;&#x1f92f;努力赶上周围卷王的脚步。魔王的主页&#xff1a;&#x1f525;&#x1f525;&#x1f525;大魔王.&#x1f525;&#x1f525;&#x1f525; ❤️‍&#x1…