【数据结构】线段树

算法提高课笔记 还未更新完

文章目录

  • 原理
  • pushup
  • build
  • modify
  • query
  • pushdown(懒标记 / 延迟标记)
  • 扫描线法

原理

时间复杂度:O(logn)

线段树是一棵二叉树,把一段区间分成多个部分
在这里插入图片描述
类似堆的方式,用一维数组存整棵树

对于编号x的结点:

  • 父结点 ⌊ x ⌋ \lfloor x \rfloor x,表示为 x >> 1
  • 左子树 2 x 2x 2x,表示为 x << 1
  • 右子树 2 x + 1 2x+1 2x+1,表示为 x << 1 | 1

对于长度为n的区间,最坏估计有 4 n − 1 4n-1 4n1 个结点,因此 开数组时空间一般开 4 n 4n 4n

pushup

由子结点计算父结点的信息

模板:

// u表示当前树中结点编号 lr表示树中结点左右子结点
void pushup(Node &u, Node &l, Node &r)
{/* 此处用[l]和[r]的值更新[u] */
}void pushup(int u)
{pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

build

将一段区间初始化为线段树

  1. 首先记录下当前区间的左右端点,如果左端点和右端点相等就直接返回
  2. 如果不相等,取中间值 mid,然后分别递归左右两段

模板:

// u表示当前树中结点编号 lr表示区间左右端点
void build(int u, int l, int r)
{if (l == r) // 左右端点相同表示到达叶子结点{tr[u] = {    }; // 创建该结点}else{tr[u].l = l, tr[u].r = r;int mid = l + r >> 1; // 取中间值build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); // 分别构造左右两棵子树pushup(u); // 利用pushup更新该点}
}

modify

修改单点或区间(需要用到push_down操作)

修改单点模板:

// u为当前树中结点编号 要把x位置的值更新为v
void modify(int u, int x, int v)
{if (tr[u].l == x && tr[u].r == x) // 到达叶子结点 直接更新{tr[u] = {     };}else{int mid = tr[u].l + tr[u].r >> 1; // 取中间值if (x <= mid) modify(u << 1, x, v); // 要更新的位置在左半部分else modify(u << 1 | 1, x, v); // 要更新的位置在右半部分pushup(u); // 更新此位置结点}
}

修改区间模板:

void modify(int u, int l, int r, int d)
{if (tr[u].l >= l && tr[u].r <= r) // 当前树中结点在所求区间之内{tr[u].sum += (i64)(tr[u].r - tr[u].l + 1) * d; // 更新区间信息tr[u].add += d; // 打上懒标记}else // 当前树中结点不在所求区间之内{pushdown(u); // 将懒标记向下传递int mid = tr[u].l + tr[u].r >> 1;if (l <= mid) modify(u << 1, l, r, d); // 与左半段有重合部分就更新左半段if (r > mid) modify(u << 1 | 1, l, r, d); // 与左半段有重合部分就更新左半段pushup(u); // 由于modify修改了区间结点的信息,所以被修改的结点的祖先结点都需要重算一遍}
}

query

查询区间信息

假设我们要查询某区间的最大值

定义 [l, r] 为我们要查询的区间,[Tl, Tr] 为树中结点(当前我们正在维护的区间),这两个区间会有如下两种关系:

  • [ T l , T r ] ⊂ [ l , r ] [Tl, Tr]\subset[l, r] [Tl,Tr][l,r],树中结点完全包含在要查询的区间内部
    这种情况直接返回当前区间最大值即可
  • [ l , r ] ⋂ [ T l , T r ] ≠ ∅ [l, r]\bigcap[Tl, Tr]\not=\emptyset [l,r][Tl,Tr]=,二者有交集
    和左边有交集就递归到左边做一遍,和右边有交集就递归到右边做一遍
    l > mid只递归右边,r <= mid只递归左边,否则左右都递归

模板:

Node query(int u, int l, int r)
{if (tr[u].l >= l && tr[u].r <= r) return tr[u]; // 当前区间在被查询区间之内 直接返回else{int mid = tr[u].l + tr[u].r >> 1; // 取中间值if (r <= mid) return query(u << 1, l, r); // 被查询区间在当前区间左半部分else if (l > mid) return query(u << 1 | 1, l, r); // 被查询区间在当前区间右半部分else // 被查询区间横跨当前区间的左右两部分{auto left = query(u << 1, l, r); // 计算出左半部分值auto right = query(u << 1 | 1, l, r); // 计算出右半部分值Node res;pushup(res, left, right); // 更新结果return res;}}
}

pushdown(懒标记 / 延迟标记)

将父结点的修改更新到子结点

单点修改可以只用pushup,涉及到区间修改就需要使用pushdown

懒标记 :在当前树中结点上打上懒标记,就表示对以当前树中结点为根结点每一个子树都进行操作(根结点自己不用操作)

那么懒标记怎么进行传递呢?

焗个栗子:比如我们在蓝色的这一段区间上打上懒标记
在这里插入图片描述
每当我们需要遍历蓝色区间结点下方的子结点时,我们就把懒标记传递给下一层结点,同时把根结点的懒标记删除,就像这样:
在这里插入图片描述
当然,除了传递标记,我们还需要对线段树中记录的值进行更新,比如说这个线段树记录的是区间和,打上懒标记表示这一段区间每一个数都要加上a,那么我们在传递懒标记的同时,还需要让下方结点的区间和加上(r - l + 1) * a,其中(l - r + 1)表示下方被更新结点的区间长度

以此类推,每当我们需要遍历下方结点时,就把懒标记向下传,并更新下方结点的值

以上就是pushdown操作的基本内容

模板:

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 += (i64)(left.r - left.l + 1) * root.add;right.add += root.add, right.sum += (i64)(right.r - right.l + 1) * root.add;root.add = 0;}
}

扫描线法

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

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

相关文章

计算机导论实验——Linux基础入门

使用Xshell登录 Linux 主机 linux命令&#xff1a; cd&#xff1a;去哪里 pwd&#xff1a;在哪里 ls&#xff1a;查看当前有什么文件 mkdir&#xff1a;创建新目录 cp&#xff1a;复制 cat&#xff1a;连接或显示文件 rm&#xff1a;删除 mv&#xff1a;用于移动或重命名文件…

Android Studio版本升级后的问题 gradle降级、jdk升级

Cannot use TaskAction annotation on method IncrementalTask.taskAction$gradle_core() because interface org.gradle.api.tasks.incremental.IncrementalTaskInputs is not a valid parameter to an action method. 修改下面两处地方分别为7.0.3、7.3.3Android Gradle plu…

Spark中的Driver、Executor、Stage、TaskSet、DAGScheduler等介绍

工作流程&#xff1a; Driver 创建 SparkSession 并将应用程序转化为执行计划&#xff0c;将作业划分为多个 Stage&#xff0c;并创建相应的 TaskSet。Driver 将 TaskSet 发送给 TaskScheduler 进行调度和执行。TaskScheduler 根据资源情况将任务分发给可用的 Executor 进程执…

基于 chinese-roberta-wwm-ext 微调训练中文命名实体识别任务

一、模型和数据集介绍 1.1 预训练模型 chinese-roberta-wwm-ext 是基于 RoBERTa 架构下开发&#xff0c;其中 wwm 代表 Whole Word Masking&#xff0c;即对整个词进行掩码处理&#xff0c;通过这种方式&#xff0c;模型能够更好地理解上下文和语义关联&#xff0c;提高中文文…

数据仓库Hive(林子雨课程慕课)

文章目录 9.数据仓库Hive9.1 数据仓库的概念9.2 Hive简介9.3 SQL语句转换为MapReduce作业的基本原理9.4 Impla9.4.1 Impala简介9.4.2 Impala系统架构9.4.3 Impala查询执行过程9.4.4 Impala与Hive的比较 9.5 Hive的安装和基本操作9.5.1 Hive安装9.5.2 Hive基本操作 9.数据仓库Hi…

黑马点评-05缓存穿透问题及其解决方案,缓存空字符串或使用布隆过滤器

缓存穿透问题(缓存空) 缓存穿透的解决方案 缓存穿透(数据穿透缓存直击数据库): 缓存穿透是指客户端请求访问缓存中和数据库中都不存在的数据,此时缓存永远不会生效并且用户的请求都会打到数据库 数据库能够承载的并发不如Redis这么高&#xff0c;如果大量的请求同时访问这种…

十大排序算法Java实现及时间复杂度

文章目录 十大排序算法选择排序冒泡排序插入排序希尔排序快速排序归并排序堆排序计数排序基数排序桶排序时间复杂度 参考资料 十大排序算法 选择排序 原理 从待排序的数据元素中找出最小或最大的一个元素&#xff0c;存放在序列的起始位置&#xff0c; 然后再从剩余的未排序元…

C++类和对象(下)

目录 一、初始化列表 二、单参构造参数和explicit关键字 三、匿名对象 四、static成员 五、友元 六、内部类 一、初始化列表 之前我们在构造函数中写得还不错&#xff0c;也没发现什么问题&#xff0c;为什么C还有搞一个初始化列表呢&#xff1f; 如下这段代码&#x…

mars3d的api文档关于addDynamicPosition查找使用说明

示例链接&#xff1a;功能示例(Vue版) | Mars3D三维可视化平台 | 火星科技 api地址&#xff1a;Mars3D三维可视化平台 | 火星科技 说明&#xff1a; 1.用户反馈不知道如何搜索这个属性的用法 说明&#xff1a; 1. 示例代码中的graphic.addDynamicPosition()说明这个addDynam…

基本微信小程序的二手车交易平台

项目介绍 首先,论文一开始便是清楚的论述了小程序的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,更进一步明确系统的需求。然后在明白了小程序的需求基础上需要进一步地设计系统,主要包罗软件架构模式、整体功能模块、数…

@MultipartConfig注解

前言&#xff1a; 在学习Javaweb的Servlet文件上传和下载的过程中&#xff0c;我们会遇到一个特殊的注解---MultipartConfig。 MultipartConfig的适用情况&#xff1a; 1.文件上传: 当您的应用程序需要接收用户上传的文件时&#xff0c;可以在相应的 Servlet 上使用 Multipart…

Jmeter连接mysql数据库详细步骤

一、一般平常工作中使用jmeter 连接数据库的作用 主要包括&#xff1a; 1、本身对数据库进行测试&#xff08;功能、性能测试&#xff09;时会需要使用jmeter连接数据库 2、功能测试时&#xff0c;测试出来的结果需要和数据库中的数据进行对比是否正确一致。这时候可以通过j…

C++ 位图与布隆过滤器

目录 前言位图场景演示应用场景模拟实现问题例题 布隆过滤器例子理解应用 例题 前言 位图与布隆过滤器是用来在海量数据中判断一个数据在不在的问题的数据结构&#xff0c;这种数据结构在存储空间上大大的优于红黑树、哈希等数据结构 位图 我们为了处理一个数据在海量数据中…

SQL开发笔记之专栏介绍

Sql是用于访问和处理数据库的标准计算机语言&#xff0c;使用SQL访问和处理数据系统中的数据&#xff0c;这类数据库包括&#xff1a;Mysql、PostgresSql、Oracle、Sybase、DB2等等&#xff0c;数据库无非围绕着“增删改查”的核心业务进行开发。并且目前绝大多数的后端程序开发…

很烦的Node报错积累

目录 1. 卡在sill idealTree buildDeps2、Node Sass老是安装不上的问题3、unable to resolve dependency tree4、nvm相关命令5、设置淘宝镜像等基操5.1 镜像 5.2 npm清理缓存6、Browserslist: caniuse-lite is outdated loader 1. 卡在sill idealTree buildDeps 参考&#xf…

国际通用的Bug管理工具推荐:多款工具助力项目开发与管理

国际通用的bug管理工具有&#xff1a;1、Zoho Projects&#xff1b;2、Tracup&#xff1b;3、Bugtags&#xff1b;4、QC(QualityCenter)&#xff1b;5、Bugzilla&#xff1b;6、EasyBUG&#xff1b;7、Mantis&#xff1b;8、WebIssues。Zoho Projects拥有专业的缺陷管理模块&am…

复数的三角形式与指数形式

See https://blog.csdn.net/u011089570/article/details/102685877

深入了解基数排序:原理、性能分析与 Java 实现

基数排序&#xff08;Radix Sort&#xff09;是一种非比较性排序算法&#xff0c;它根据元素的每个位上的值来进行排序。基数排序适用于整数或字符串等数据类型的排序。本文将详细介绍基数排序的原理、性能分析及java实现。 基数排序原理 基数排序的基本原理是按照低位先排序&…

android 固定进度环形刷新效果

android 固定进度无限旋转的环形效果 效果图 Activity 中使用 val rotation: ObjectAnimator ObjectAnimator.ofFloat(progressBar, "rotation", 0f, 360f) rotation.duration 000 // 旋转持续时间为2秒 rotation.repeatCount ObjectAnimator.INFINITE // 设置为…

保姆式教程:MAC安装Android studio(包括安装JDK,Android SDK),解决gradle下载慢的问题

文章目录 参考文章安装JDK并配置环境变量安装JDK配置JDK相关的环境变量 Android studio 安装下载Android studiogradle下载慢解决方法 安装Android SDK选择jdk版本安装SDK并配置环境变量 参考文章 原文链接 原文链接 安装JDK并配置环境变量 安装JDK 下载地址 下载后双击安装…