线段树详解 原理解释 + 构建步骤 + 代码(带模板)
目录
介绍:
定义:
以具体一个题目为例:
树的表示方法:
实现步骤:
构建结点属性:
pushup函数:
build函数:
pushdown函数:
modify函数:
query函数:
如何记忆:
模板:
介绍:
线段树(Segment Tree)是一种常用的数据结构,用于解决涉及区间查询的问题。它主要用于在数组或列表等数据结构上支持以下两类查询操作:
- 区间查询:查询某个区间内的统计信息,例如求和、最大值、最小值等。
- 区间更新:修改数组中某个区间元素的值,并相应地更新线段树中的信息。
核心思想是将原始数据递归地划分成一系列不相交的区间,并在每个区间上维护一些预先计算好的信息,以支持高效的区间查询。
定义:
假设我们有一个包含 N 个元素的数组 A,线段树 T 是基于数组 A 的线段树。线段树 T 是一个满二叉树,它具有以下性质:
- 根节点表示整个数组的区间 [1, N]。
- 如果一个节点表示的区间是 [left, right],则它的左子节点表示的区间是 [left, mid],右子节点表示的区间是 [mid+1, right],其中 mid 是 left 和 right 的中间值。
- 叶子节点表示数组 A 中的单个元素,而内部节点表示对应区间上的预计算信息(如区间和、区间最大值等)。
- 线段树通常使用数组来模拟实现。
线段树算法一般包含以下五个函数:
1.build(); 初始构建一个线段树。
2.pushpu(); 向上传递信息。
3.pushdown(); 向下传递懒标记,并且更新子树。
4.modify(); 修改某一区间。
5.query(); 查询某一区间信息。
下面我们一个一个来介绍。
以具体一个题目为例:
下面解析以此题目为例子。
树的表示方法:
我们用 tr 数组来模拟这颗树。
假设根节点在 tr 数组中的的下标为为 i,那么其左右子树的下标为:
左:i * 2 (i << 1)
右:i * 2 + 1 (i << 1 | 1)
我们一般使用位运算,也就是括号里的,含义是一样的。所以可以计算出,tr 数组的长度最多就是题目所给数组长度的4倍。
实现步骤:
事先把输入的数组存在 w数组 中。
构建结点属性:
树结点其实就是一个区间,所以属性包含:左右边界,懒标记。
此题的懒标记就是区间需要加上的值 d 。
根据题目我们还需要查询区间的元素和,所以在其中添加一个 sum。
struct Node
{int l, r;LL sum;LL add; // 懒标记
}tr[N * 4];;
pushup函数:
我们在 build 一颗树之前,要先写 pushup 函数,用于向上传递信息,因为我们只知道叶子结点的值,我们要用后序遍历去构建父亲,所以要用到 pushup ,根据题目,我们要向上传递的信息显然是左右子树的 sum 和,这样就可以算出父亲的 sum 。
void pushup(int u) // 向上传递信息
{tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
build函数:
接下来我们开始构建这颗树,若区间内只有一个元素(l == r),说明我们找到了叶子结点,给叶子结点赋值,若不是结子节点(l != r),就继续向左右子树递归,在递归完成时(后序遍历)使用pushup,通过已经获得值的子树去更新父亲。
void build(int u, int l, int r)
{if (l == r) tr[u] = {l, r, w[l], 0}; // 叶子节点else{tr[u] = {l, r};int mid = l + r >> 1;build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); // 若不是叶子节点,向下递归pushup(u); // 通过子树构建父亲}
}
pushdown函数:
pushdown函数是给子树传递懒标记的,如果懒标记不为空,就将父亲的懒标记传递给左右子树,并且通过懒标记更新左右子树的信息,此题求的是sum,所以子树的 sum 值就要加上区间长度乘上父亲的懒标记,最后清空父亲的懒标记。
注意:懒标记表示的子树需要添加的信息,不包含父亲自己,所以在传递懒标记时,才要传递懒标记同时更新子树。
void pushdown(int u)
{auto &root = tr[u], &left = tr[u << 1], & right = tr[u << 1 | 1];if (root.add){// 传递懒标记并且更新子树left.add += root.add, left.sum += (LL)(left.r - left.l + 1) * root.add;right.add += root.add, right.sum += (LL)(right.r - right.l + 1) * root.add;root.add = 0; // 删除懒标记}
}
modify函数:
修改区间信息,如果当前遍历的结点区间已经在区间中,那么就直接给其加上懒标记,并且计算更新其 sum。如果当前遍历的结点区间中的一部分是需要修改的区间,那么就先向下传递懒标记pushdown,然后在向需要修改的左右子树去递归,后序返回时,要给更新父亲pushup。
void modify(int u, int l, int r, int v)
{// 结点在要修改的区间中if (l <= tr[u].l && r >= tr[u].r){tr[u].sum += (tr[u].r - tr[u].l + 1) * v;tr[u].add += v; // 加上懒标记}else{pushdown(u); // 先传递懒标记int mid = tr[u].l + tr[u].r >> 1;if (l <= mid) modify(u << 1, l, r, v);if (r > mid) modify(u << 1 | 1, l, r, v);pushup(u); // 更新父亲}
}
query函数:
用于查询区间信息,这里就是查询区间的sum。若遍历到的结点区间在查询区间之中,就返回其sum,若结点区间只有一部分在查询区间中,一样的,也是先传递懒标记,然后继续向需要计算的左右子树去递归,后序返回时计算结果。
LL query(int u, int l, int r)
{if (l <= tr[u].l && r >= tr[u].r) return tr[u].sum; // 返回区间信息pushdown(u); // 也是先传递懒标记LL v = 0;int mid = tr[u].l + tr[u].r >> 1;if (l <= mid) v = query(u << 1, l, r);if (r > mid) v += query(u << 1 | 1, l, r);return v;
}
如何记忆:
最重要的是注意每个函数pushup,pushdown函数的位置。只有在modify函数才两个一起用。
build函数只用一个pushup,query函数只用一个pushdown。
模板:
根据具体题目,自行修改。
// 操作是给区间每一个数加d
// 询问是求某一区间和
#include<iostream>using namespace std;typedef long long LL;
const int N = 100010;int w[N];
int n, m;struct Node
{int l, r;LL sum;LL add; // 懒标记
}tr[N * 4];;void pushup(int u) // 向上传递信息
{tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u)
{auto &root = tr[u], &left = tr[u << 1], & right = tr[u << 1 | 1];if (root.add){// 传递懒标记并且更新子树left.add += root.add, left.sum += (LL)(left.r - left.l + 1) * root.add;right.add += root.add, right.sum += (LL)(right.r - right.l + 1) * root.add;root.add = 0; // 删除懒标记}
}
void build(int u, int l, int r)
{if (l == r) tr[u] = {l, r, w[l], 0}; // 叶子节点else{tr[u] = {l, r};int mid = l + r >> 1;build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); // 若不是叶子节点,向下递归pushup(u); // 通过子树构建父亲}
}
void modify(int u, int l, int r, int v)
{// 结点在要修改的区间中if (l <= tr[u].l && r >= tr[u].r){tr[u].sum += (tr[u].r - tr[u].l + 1) * v;tr[u].add += v; // 加上懒标记}else{pushdown(u); // 先传递懒标记int mid = tr[u].l + tr[u].r >> 1;if (l <= mid) modify(u << 1, l, r, v);if (r > mid) modify(u << 1 | 1, l, r, v);pushup(u); // 更新父亲}
}LL query(int u, int l, int r)
{if (l <= tr[u].l && r >= tr[u].r) return tr[u].sum; // 返回区间信息pushdown(u); // 也是先传递懒标记LL v = 0;int mid = tr[u].l + tr[u].r >> 1;if (l <= mid) v = query(u << 1, l, r);if (r > mid) v += query(u << 1 | 1, l, r);return v;
}int main()
{scanf("%d%d", &n, &m);for (int i = 1; i <= n; ++i) scanf("%d", &w[i]); // 读入数组build(1, 1, n); // 以1为根节点,1~n区间建树char op[2];int l, r, t;// 读入修改和查询,q是查询,否则是修改while (m -- ){scanf("%s%d%d", op, &l, &r);if (*op == 'Q') printf("%lld\n", query(1, l, r));else{scanf("%d", &t);modify(1, l, r, t);}}return 0;
}相关文章:
线段树详解 原理解释 + 构建步骤 + 代码(带模板)
目录 介绍: 定义: 以具体一个题目为例: 树的表示方法: 实现步骤: 构建结点属性: pushup函数: build函数: pushdown函数: modify函数: query…...
Java中Timer的使用
Timer 简述 在Java中,Timer(计时器)是一个用于安排定时任务的类。它可以实现在指定的时间间隔或指定的时间点执行某项任务或操作。 简单的来说Timer就是在Java中用来实现定时任务的工具。 Timer的API Timer中有两API可以使用分别是schedule…...
关于EJB,这两文把热闹和门道都说清楚了
关于技术的很多概念,如果你是小白,不建议看官网。原因就在于官网描述太抽象,就像八股文,看完感觉好像说了很多,但回过头又感觉似乎啥都没说。太虚、不接地气,是最大毛病。其实这些官网的打太极式的表述&…...
MixFormerV2: Efficient Fully Transformer Tracking
摘要 基于变压器的跟踪器在标准基准测试上取得了很强的精度。然而,它们的效率仍然是在GPU和CPU平台上实际部署的一个障碍。在本文中,为了克服这一问题,我们提出了一个完全变压器跟踪框架,称为MixFormerV2,没有任何密集…...
K8S中网络如何通信
Kubernetes 提出了一个自己的网络模型“IP-per-pod”,能够很好地适应集群系统的网络需求,它有下面的这 4 点基本假设: 集群里的每个 Pod 都会有唯一的一个 IP 地址。Pod 里的所有容器共享这个 IP 地址。集群里的所有 Pod 都属于同一个网段。…...
LangChain Agents深入剖析及源码解密上(三)
AutoGPT案例V1版本 AutoGPT是一个实验性的开源应用程序,展示了GPT-4语言模型的功能,AutoGPT程序由GPT-4驱动,将大语言模型的思考链接在一起,以自主实现设定的任何目标。作为GPT-4完全自主运行的首批例子之一,AutoGPT突破了人工智能的可能性。LangChain框架复现了https://g…...
分布式限流方案及实现
优质博文:IT-BLOG-CN 一、限流的作用和意义 限流是对高并发访问进行限制,限速的过程。通过限流来限制资源,可以提高系统的稳定性和可靠性,控制系统的负载,削峰填谷,保证服务质量。 服务限流后的常见处理…...
vuejs源码阅读之优化器
前面讲过vuejs中解析器是把html模版解析成AST,而优化器的作用是在AST中找到静态子树并打上标记。 静态子树是指的那些在AST中永远不会发生变化的节点。 例如,一个纯文本节点就是静态子树,而带变量的文本节点就不是静态子树,因为…...
【C++】-动态内存管理
作者:小树苗渴望变成参天大树 作者宣言:认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧! 文章目录 前言一、C内存管理方式1.1 new/delete操作内置类型 总结 前言 今天再讲一个…...
微服务SpringCloud教程——微服务是什么
微服务(MicroServices)最初是由 Martin Fowler 于 2014 年发表的论文《MicroServices》中提出的名词,它一经提出就成为了技术圈的热门话题。 微服务,我们可以从字面上去理解,即“微小的服务”,下面我们从“…...
RNN架构解析——LSTM模型
目录 LSTMLSTM内部结构图 Bi-LSTM实现 优点和缺点 LSTM LSTM内部结构图 Bi-LSTM 实现 优点和缺点...
苹果电脑系统优化工具:Ventura Cache Cleaner for mac
Ventura Cache Cleaner for Mac是一款专门为苹果电脑开发的系统优化工具,旨在帮助用户清理和优化Mac电脑,提高系统性能和速度。该软件由美国公司Northern Softworks开发,已经推出了多个版本,适用于不同版本的Mac操作系统。 Ventu…...
为了爱人穿越沙漠-心理测试
我觉得很准的一个心理测试。我的答案反射出我的态度,它们是100%的贴切。有兴趣的朋友也不妨一试。 你有一个深爱着的心上人,然而你们却被一片无垠的沙漠相隔两地,你禁不住思念的折磨,决定穿越沙漠去寻找你心中的那个爱人…… 1、…...
SpringBoot月度员工绩效考核管理系统【附任务书|ppt|万字文档(LW)和搭建文档】
主要功能 员工登录: ①首页、个人中心:修改密码、个人信息管理等 ②公告信息管理、绩效指标管理、绩效考核管理 管理员登录: ①首页、个人中心:修改密码、个人信息管理等 ②公告信息管理、部门管理、岗位管理、员工管理、绩效指标…...
【新星计划】STM32F103C8T6 - C语言 - 蓝牙JDY-31-SPP串口通信实验
文章目录 蓝牙技术的发展历史SPP蓝牙串口BLE协议(超低功耗应用蓝牙协议) 常见通用蓝牙模块JDY-31-SPPHC05/06 Keil 工程开发模版main.c 源文件:接线方式:烧录工具:FlyMcu串口调试工具:XCOM蓝牙调试助手APP …...
算法39:Excel 表列序号
一、需求 给你一个字符串 columnTitle ,表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。 例如: A -> 1 B -> 2 C -> 3 … Z -> 26 AA -> 27 AB -> 28 … 示例 1: 输入: columnTitle “A” 输出: 1 示例 2&…...
Android:ImageView xml方式配置selector 图片切换
1、在res/drawable目录下创建一个新的XML文件,比如selector_image.xml <?xml version"1.0" encoding"utf-8"?> <selector xmlns:android"http://schemas.android.com/apk/res/android"> <!-- 背景选择器 state_pre…...
Spring Boot 缓存 Cache 入门
Spring Boot 缓存 Cache 入门 1.概述 在系统访问量越来越大之后,往往最先出现瓶颈的往往是数据库。而为了减少数据库的压力,我们可以选择让产品砍掉消耗数据库性能的需求。 当然也可以引入缓存,在引入缓存之后,我们的读操作的代码ÿ…...
如何关闭谷歌浏览器自动更新
适用范围: 写自动化脚本时,需要安装浏览器驱动,安装浏览器驱动时需要下载对应的浏览器驱动版本,如果浏览器版本一直在自动更新的话,自动化脚本会报错浏览器版本和浏览器驱动不匹配,所以建议关闭谷歌浏览器自动更新&am…...
mybatis日志工厂
前言: 如果一个数据库操作,出现异常,我们需要排错,日志就是最好的助手 官方给我们提供了logImpl:指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 默认工厂: 在配置文件里添加…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...
【深尚想】TPS54618CQRTERQ1汽车级同步降压转换器电源芯片全面解析
1. 元器件定义与技术特点 TPS54618CQRTERQ1 是德州仪器(TI)推出的一款 汽车级同步降压转换器(DC-DC开关稳压器),属于高性能电源管理芯片。核心特性包括: 输入电压范围:2.95V–6V,输…...
Yii2项目自动向GitLab上报Bug
Yii2 项目自动上报Bug 原理 yii2在程序报错时, 会执行指定action, 通过重写ErrorAction, 实现Bug自动提交至GitLab的issue 步骤 配置SiteController中的actions方法 public function actions(){return [error > [class > app\helpers\web\ErrorAction,],];}重写Error…...
