线段树专题(1)

线段树

线段树可维护的信息类型

        线段树可以维护的信息类型,父范围上的某个信息,可以用O(1)的时间,从子范围的信息加工得到,例如求某个范围的最大最小值,给某个范围每个位置加相同的数字,进行求和。

0到2范围上的最大值是5,3到5范围上的最大值是7,如果求0到5范围的最大值,就是5和7进行比较,这样子就很快。 已知2到4范围上的和是15,那么给这三个位置的值都增加2,那么和就是15+2*3=21,也很快;
        那么还有一些不能够被维护的信息类型,例如:某个范围出现次数最多的数字

 对于0到4范围上,出现最多的数字是3,5到9范围上,出现最多的数字是1,但是在0到9范围上出现的最多的数字不是1和3,而是5,父范围上的信息,不能够O(1)的时间由子范围得到,那么这样子的就是不能被线段树维护的信息。

线段树的经典功能:范围查询和范围修改操作单次调用的时间复杂度为O(logN)

线段树的操作

以经典的累加和为例:

  1. 线段树可以1下标开始,也可以0下标开始,1下标开始是经典的设定。
  2. 线段树可以在初始化时,指定范围的规模[1,n],一旦确定不能改变
  3. 任何一个大范围[l,r],严格从中点拆分为左[l,mid]和右[mid+1,r]
  4. 每个范围的信息,填在独立的,连续的数组中,最大范围的[1,n]填在sum[1]
  5. 如果父信息在sum[i],那么左范围sum[i*2],右范围[i*2+1]
  6. 范围[l,r]和i值对应,由递归去维护,无需记录对应关系

 对于维护节点个数不是2^n次方的范围来说

 由于子范围和父范围有相对应的关系,那么可以不用记忆一个范围对应在sum数组中的什么位置,直接由父信息在sum[i],那么左范围sum[i*2],右范围[i*2+1]进行维护,那么实际上应该开多大的数组?2*n+1?不可以,用实际的例子展示下。

数组多大空间

如图:这样子超出了2*n+1的范围

 那么应该如何去做,求得实际的空间。应该是根据树高去求得节点的个数如果节点个数是2^n次方,那么展开就是一颗完全二叉树,树的高度(log以2为底n的对数+1,)对于不是2^n节点的,需要的节点个数是大于当前节点且最接近的节点个数,那么树高就是log以2为底n的对数+2,因此可以得出需要开辟的空间为4*n,也可以通过代码进行演示

// [l,r]在数组的i位置
// 返回递归过程展开的最大编号
int maxi(int l, int r, int i) {if (l == r) {// 递归到了1个节点,直接填return i;}else {// 不止一个int mid = (l + r) >> 1;// i << 1 | 1  -->  (i * 2 + 1) return max(maxi(l, mid, i << 1), maxi(mid + 1, r, i << 1 | 1));}
}int main() {int n = 10000;	// 测试范围int a = 0;		// 最大倍数的iint b = 0;		// 最大倍数的需要的空间double t = 0;	// 最大倍数for (int i = 1; i <= n; i++) {int space = maxi(1, i, 1);	// 获取最大编号double times = space / (double)i;	// 倍数cout << "范围[1~" << i << "]," << "需要空间" << space << ",倍数=" << times << endl;if (times > t) {a = i;b = space;t = times;}}cout << "其中的最大倍数,范围[1~" << a << "]," << "需要空间" << b << ",倍数=" << t << endl;return 0;
}

建树

void up(int i) {// 父范围的累加和 = 左范围累加和 + 右范围累加和sum[i] = sum[i << 1] + sum[i << 1 | 1];
}// 建树
void build(int l, int r, int i) {if (l == r) {// 只有一个,直接填sum[i] = arr[l];}else {int mid = (l + r) >> 1;// 左右调用求和build(l, mid, i << 1);build(mid + 1, r, i << 1 | 1);up(i);}
}

查询

query(jobl,jobr,l,r,i):jobl和jobr代表需要查询任务的范围,l和r代表当前在的范围,i代表当前是sum数组的哪一个位置。

 例如:求得[2,7]范围上的和。query(2,7,1,8,1),从顶部开始向下寻找,[1,4]位置包含,拆分为[1,2]和[3,4],对于[1,2]只有右边的2在[2,7]中,向上返回sum中9位置的数字,对于[3,4]都包含在[2,7]中,向上返回sum中5位置的数字,数字相加返回到顶部;同理,[5,8]范围包含,得到sum中6位置的数字和sum中14位置的数字,数字相加返回到顶部。
        从这个过程可以看出,某些位置可以不遍历到树的根节点,因此大大节省了时间。时间复杂度为logn,对于根节点的两边展开,大致是沿着一条线下去的,部分返回的过程中不需要完全展开到根节点。

// 任务范围[jobl,jobr]
// 线段树维护范围[l,r]
// 在sum中的位置i,在[l,r]的和是i
long query(int jobl, int jobr, int l, int r, int i) {if (jobl <= l && r <= jobr) {// 范围全命中,不用向下展开了// 需要[2,5],现在是[3,4]return sum[i];}// 没有全命中,任务下发 int mid = (l + r) >> 1;// 信息下发 -- 后面的方法讲了信息下发的过程// 如果没有down,可能更新不对,例如查询[2,3]范围的和// 假设上次的懒更新到了[1,2]和[3,4],没有往下更新,那么进行查询时// 获取到的2和3位置的值是错误的,即add数组相关位置是错误的down(i, mid - l + 1, r - mid);long ans = 0;if (jobl <= mid) {// 任务命中,左侧还需要调用ans += query(jobl, jobr, l, mid, i << 1);}if (jobr > mid) {// 任务命中,右侧还需要调用ans += query(jobl, jobr, mid + 1, r, i << 1 | 1);}return ans;
}

懒更新

void add(jobl,jobr,jobv,l,r,i):在[jobl,jobr]范围上,每个数组增加jobv;来到[l,r]范围上,信息存储的位置是i.

调用add(jobl,jobr,jobv,1,n,1),范围增加的递归,懒更新机制

  • 任务[jobl,jobr]把当前范围[l,r]全覆盖,不在向下传递任务
    add[i] += jobv;sum[i] += jobv * (r - l  + 1)
  • 如果任务不能把当前范围全包,把当前范围积攒的懒信息只往下发一层,发过了不必要重复发送,然后决定往左有范围,进行递归,递归完成后,利用左右sum信息,把当前范围的sum[i]信息修改正确
  • 退出递归

当原数组长的是8,每个位置是0,可以构建出15个节点的线段树,现在在某个范围上增加相同的值。需要add数组和sum数组,长度为线段树节点的个数(15),add数组存储某位置或者某段位置增加的数值,sum数组存储进行更新后得到的增加的和。
初始状态:

 进行[1,8]位置增加5:由于根节点位置包含在[1,8]中,因此只更新数组的1位置,得到:

[5,6]位置增加3:由于[5,6]没有把[1,8]覆盖掉,因此要进行懒更新,将积攒的信息发下去:add[2] = 5,add[3] = 5,add[1] = 0,由于add[2]和add[3]进行更新,那么sum数组对应的sum[2] = 20,sum[]3] = 20,总和不变,因此sum[1]不变,还是40 。[1,4]中没有[5,6],不用向下,[5,6]在[5,8]但是没有被完全覆盖,因此进行懒更新下发:add[6] = 5,add[7] = 5,同理sum数组进行更新sum[6] = 10,sum[7] = 10.

 此时[5,6]范围包住了需要增加的[5,6],直接进行更新,不用下发了,进行更新,add[6] = 5+3;对应的sum[6] = 10+2*3;那么sum[3] = 20+2*3;sum[1] = 40+2*3得到:

[2,6]位置增加-2:没有被[1,8]完全包住,由于上次已经进行了懒更新下发,因此直接到[1,4]和[5,8]位置,没有被[1,4]完全包住,进行懒更新下发,没有被[5,8]完全包住,由于已经下发,直接到下一层。
 

 同理[1,2]进行更新

左侧无懒更新下发,进行数值更新,add[9] = 5 - 2,add[5] = 5- 2 ,相对应的sum进行更新得到如下表格 :

 右侧更新,add[6] = 8 - 2 ,sum更新。任务完成

 

// 来到[l,r]对应的下标是i,范围上数字个数n == r - l + 1
void lazy(int i, long v, int n) {// sum和add进行调整sum[i] += v * n;add[i] += v;
}void up(int i) {// 父范围的累加和 = 左范围累加和 + 右范围累加和sum[i] = sum[i << 1] + sum[i << 1 | 1];
}// 懒信息的下发
// [l,r] i 懒信息
// [l,mid] i * 2   ln表示左侧几个数
// [mid+1,r] i*2+1 rn表示右侧几个数
void down(int i, int ln, int rn) {// 留着信息才下发if (add[i] != 0) {// 发左lazy(i << 1, add[i], ln);// 发右lazy(i << 1 | 1, add[i], rn);// 父范围懒信息清空add[i] = 0;}
}// jobl ~ jobr范围上每个数字增加jobv
void add(int jobl, int jobr, long jobv, int l, int r, int i) {// 当前任务把整个范围全包了,不用往下发了if (jobl <= l && r <= jobr) {lazy(i, jobv, r - l + 1);}else {// 没有全包int mid = (l + r) >> 1;// 懒更新下发,下发左和下发右down(i, mid - l + 1, r - mid);if (jobl <= mid) {// 左边add(jobl, jobr, jobv, l, mid, i << 1);}if (jobr > mid) {// 右边add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1);}// 信息汇总up(i);}
}

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

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

相关文章

NoETL自动化指标平台为数据分析提质增效,驱动业务决策

直觉判断往往来源于多年的经验和专业知识&#xff0c;能够在复杂和不确定的环境中快速做出决策反应。但这种方式普遍存在主观偏见&#xff0c;缺乏合理的科学依据&#xff0c;无法全面、客观、精准地评估和识别市场趋势与用户需求&#xff0c;从而造成决策失误&#xff0c;给业…

软考高级架构 - 8.1 - 系统质量属性与架构评估 - 超详细讲解+精简总结

第8章 系统质量属性与架构评估 软件系统属性包括功能属性和质量属性&#xff0c;而软件架构重点关注质量属性。 8.1 软件系统质量属性 8.1.1 概述 软件系统的质量反映了其与需求的一致性&#xff0c;即&#xff1a;软件系统的质量高低取决于它是否能满足用户提出的需求&#…

Jmeter常见的几种报错及解决方案

在性能测试的过程中&#xff0c;使用JMeter进行负载测试是一项常见而重要的任务。然而&#xff0c;测试中常常会遇到各种报错&#xff0c;这些问题可能会影响测试结果的准确性。了解这些错误的原因及解决方案&#xff0c;是每位测试工程师必备的技能 进行Jmeter项目练习的时候…

《JavaEE进阶》----21.<基于Spring图书管理系统②(图书列表+删除图书+更改图书)>

PS&#xff1a; 开闭原则 定义和背景‌ ‌开闭原则&#xff08;Open-Closed Principle, OCP&#xff09;‌&#xff0c;也称为开放封闭原则&#xff0c;是面向对象设计中的一个基本原则。该原则强调软件中的模块、类或函数应该对扩展开放&#xff0c;对修改封闭。这意味着一个软…

仿真APP助力汽车零部件厂商打造核心竞争力

汽车零部件是汽车工业的基石&#xff0c;是构成车辆的基础元素。一辆汽车通常由上万件零部件组成&#xff0c;包括发动机系统、传动系统、制动系统、电子控制系统等&#xff0c;它们共同确保了汽车的安全、可靠性及高效运行。 在汽车产业快速发展的今天&#xff0c;汽车零部件…

现代Web开发:Vue 3 组件化开发实战

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 现代Web开发&#xff1a;Vue 3 组件化开发实战 现代Web开发&#xff1a;Vue 3 组件化开发实战 现代Web开发&#xff1a;Vue 3 组…

Unity引擎智能座舱解决方案

作为全球领先的 3D 引擎之一&#xff0c;Unity引擎为车载3D HMI提供全栈支持。即为从概念设计到量产部署的整个 HMI 工作流程提供创意咨询、性能调优、项目开发等解决方案&#xff0c;从而为车载信息娱乐系统和智能驾驶座舱打造令人惊叹的交互式体验。 专为中国车企打造的HMI引…

MySQL必会知识精华6(组合WHERE子句)

我们的目标是&#xff1a;按照这一套资料学习下来&#xff0c;大家可以完成数据库增删改查的实际操作。同时轻松应对面试或者笔试题中MySQL相关题目。 上篇文章我们先做一下数据库的where条件过滤的方法&#xff0c;都是单个条件的过滤。本篇文章主要介绍查询的组合WHERE子句的…

[C++11] 可变参数模板

文章目录 基本语法及原理可变参数模板的基本语法参数包的两种类型可变参数模板的定义 sizeof... 运算符可变参数模板的实例化原理可变参数模板的意义 包扩展包扩展的基本概念包扩展的实现原理编译器如何展开参数包包扩展的高级应用 emplace 系列接口emplace_back 和 emplace 的…

欺诈文本分类检测(十八):基于llama.cpp+CPU推理

1. 前言 前文我们用Lora训练出自己的个性化模型后&#xff0c;首先面临的问题是&#xff1a;如何让模型在普通机器上跑起来&#xff1f;毕竟模型微调时都是在几十G的专用GPU上训练的&#xff0c;如果换到只有CPU的普通电脑上&#xff0c;可能会面临几秒蹦一个词的尴尬问题。 …

硬件基础06 滤波器——无源、有源(含Filter Solutions、Filter Pro、MATLAB Fdatool)

目录 一、Filter Solutions 1、软件资源及安装教程如下 2、使用相关内容 二、Filter Pro使用 1、软件资源及安装教程如下 2、使用相关内容 三、MATLAB Fdatool 1、在matlab命令中输入fdatool 2、输入相关参数&#xff0c;例如低通、FIR、20阶、hamming窗 3、调用 &am…

【HGT】文献精讲:Heterogeneous Graph Transformer

【HGT】文献精讲&#xff1a;Heterogeneous Graph Transformer 标题&#xff1a; Heterogeneous Graph Transformer &#xff08;异构图Transformer&#xff09; 作者团队&#xff1a; 加利福尼亚大学Yizhou Sun 摘要&#xff1a; 近年来&#xff0c;图神经网络&#xff08;GN…

大厂基本功 | MySQL 三大日志 ( binlog、redo log 和 undo log ) 的作用?

前言 MySQL日志 主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。其中&#xff0c;比较重要的还要属二进制日志binlog&#xff08;归档日志&#xff09;和事务日志redo log&#xff08;重做日志&#xff09;和undo log&#xff08;回滚日志&#xff09;…

【系统架构设计师(第2版)】五、软件工程基础知识

5.1 软件工程 20世纪60年代&#xff0c;为了解决软件危机&#xff0c;提出了软件工程的概念。 软件危机的具体表现&#xff1a; 软件开发进度难以预测&#xff1b;软件开发成本难以控制&#xff1b;软件功能难以满足用户期望&#xff1b;软件质量无法保证&#xff1b;软件难以…

手机内卷下一站,AI Agent

作者 | 辰纹 来源 | 洞见新研社 2024年除夕夜&#xff0c;OPPO在央视春晚即将开始前举办了一场“史上最短发布会”&#xff0c;OPPO首席产品官刘作虎宣布&#xff0c;“OPPO正式进入AI手机时代”。 春节假期刚过&#xff0c;魅族又公开表示&#xff0c;将停止“传统智能手机…

科研绘图系列:R语言组合堆积图(stacked plot)

文章目录 介绍加载R包数据数据预处理画图1画图2组合图形系统信息介绍 堆积图(Stacked Chart),也称为堆叠图,是一种常用的数据可视化图表,主要用于展示不同类别的数据量在总体中的分布情况。堆积图可以是柱状图、条形图或面积图的形式,其中各个类别的数据量被叠加在一起,…

Node.js 完全教程:从入门到精通

Node.js 完全教程&#xff1a;从入门到精通 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境&#xff0c;允许开发者在服务器端使用 JavaScript。它的非阻塞 I/O 和事件驱动架构使得 Node.js 非常适合于构建高性能的网络应用。本文将详细介绍 Node.js 的安装、基本语…

微服务day03

导入黑马商城项目 创建Mysql服务 由于已有相关项目则要关闭DockerComponent中的已开启的项目 [rootserver02 ~]# docker compose down WARN[0000] /root/docker-compose.yml: version is obsolete [] Running 4/4✔ Container nginx Removed …

每日一题之二叉树

已知结点元素值为正整数且值不相同的一棵二叉树。 该二叉树通过给出其先序遍历序列和中序遍历序列构造而成。 输入一个整数x&#xff0c;针对此二叉树编写程序求出x的右子树中所有结点值的和&#xff08;若x不在树上&#xff0c;输出-1&#xff09;。 输入说明&#xff1a;第一…

win10系统使用Visual Studio 2019或cmake编译SDL2为32位库时出现error C2118: 负下标winnt.h的解决方法

提示&#xff1a; 下图蓝体字中的VS2008是错误的&#xff0c;其实SDL.sln是用VS2010版本的软件开发的&#xff08;对于SDL-release-2.0.5.zip源码而言至少是这样&#xff0c;而2024-11-6为止SDL是2.30.9版本了&#xff0c;2.30.9版本则无需自己编译&#xff0c;只需下载带后缀…