线段树模板

article/2025/9/30 0:49:12

线段树是我接触到的第一个高级数据结构,也是我接触过的模板最长的一种代码,但他的实用性真的很强,绝大部分的区间问题都可以用线段树解决点,所以今天我打算简单介绍一下我对线段树的理解

先放一张百度来的线段树图片:

 线段树大概就长这个样子,我们可以清楚的看到,在树的每一层中,所有的区间和恰好就是整个区间,而且同一层中的不同区间不会存在交集。

先来说一下线段树可以进行的操作,可以区间修改,区间查询,单点修改,单点查询,复杂度都是(logn),这时候可能就会问了,我们通过数组进行单点修改和单点查询复杂度是o(1),为什么还要学复杂度更高的呢?这时候又会有同学说了,涉及到区间修改和查询的可以用前缀和和差分啊?但是需要注意的是前缀和和差分主要针对的是静态的,而不是动态的区间值问题. 虽然用数组单点修改和单点查询复杂度比较低,但是区间修改和区间查询复杂度都是o(n),所以总体来说复杂度就是o(n).

下面说一下线段树的几个具体操作:

看这个图有没有一种二分的感觉?其实线段树的基本思想就是二分。

我习惯是用数组实现线段树,一般需要维护4个数组

l[id]:标号为id的区间的左边界

r[id];标号为id的区间的右边界

sum[id]:标号为id的区间的所有数的和(根据题目不同,这个数组具有不同的含义)

lazy[id]:标号为id的区间的子区间需要进行的更新操作

建树代码:

void build(int id,int L,int R)
{//建树时要在一开始就初始化 l[id]=L,r[id]=R,sum[id]=0,lazy[id]=0;if(L==R){sum[id]=a[L];return ;}int mid=L+R>>1;//递归建立两个子节点 build(id<<1,L,mid);build(id<<1|1,mid+1,R);pushup(id);//用子节点更新父节点 
}

我们在建树时一开始是先递归到叶子节点再对父亲节点进行更新的,在回溯过程中我们需要进行更新操作,更新方式根据题意的不同而不同,以总和为例,更新代码:

//只有区间修改和点的修改才会加pushup操作
//id代表当前区间的编号 
void pushup(int id)
{//这里不一定是求和,还可以是其他具有共性的性质,比如最大值最小值公约数等等 sum[id]=sum[id<<1]+sum[id<<1|1];
}

接下来是单点更新操作,就是先递归找到这个点,然后进行更新,在回溯过程中对父节点更新一下就好,代码:

void update_point(int x,int id,int val)
{if(l[id]==r[id]){sum[id]=val;return ;}int mid=l[id]+r[id]>>1;pushdown(id);//题目中若无区间更新操作,则无需添加 if(x<=mid) update_point(x,id<<1,val);else update_point(x,id<<1|1,val);pushup(id);
}

单点查询操作与单点更新操作极其相似,也是先递归找到这个点,然后返回这个点的值就行了,与单点更新就差一个pushup操作,代码:

void query_point(int x,int id)
{if(l[id]==r[id]) return sum[id];int mid=l[id]+r[id]>>1;pushdown(id);//判断查询节点属于哪棵子树中 if(x<=mid) query_point(x,id<<1);else query_point(x,id<<1|1);
}

接下来就是区间更新操作了,区间更新操作与单点更新操作不同,并不是一定要递归到所需更新区间的每个点进行更新,而是直接对其子区间的sum数组进行更新,并打上懒标记,如果需要用到其子区间的sum,需要先更新再使用,这也就是懒标记的作用.下面是pushdown操作代码:

//涉及到区间修改的题目一般都需要加pushdown操作,也因此需要加lazy数组
void pushdown(int id)
{if(lazy[id]){//用父节点的lazy数组去更新子节点的lazy数组 lazy[id<<1]+=lazy[id];lazy[id<<1|1]+=lazy[id];//子节点的sum要加父节点的lazy标记乘以子节点对应区间的长度 sum[id<<1]+=(r[id<<1]-l[id<<1]+1)*lazy[id];sum[id<<1|1]+=(r[id<<1|1]-l[id<<1|1]+1)*lazy[id];lazy[id]=0;//千万不要忘记清空父节点的lazy数组 }
}

区间修改就是先递归找到目标区间的子区间,然后对其sum数组进行操作,并打上懒标记,注意找子区间前一定要进行pushdown操作,防止子区间需更新但还未更新,导致使用错误的sum数组的值.,在回溯的时候要进行pushup操作,对其父节点进行更新,下面是代码:

//区间修改中的L,R代表目标区间的左右边界 
void update_interval(int id,int L,int R,int val)
{//当前区间不在目标区间中,直接返回 if(l[id]>R||r[id]<L) return ;//当前区间是目标区间的子集,直接进行更新,做上lazy标记后就无需递归子节点 if(l[id]>=L&&r[id]<=R){sum[id]+=val*(r[id]-l[id]+1);lazy[id]+=val;return ;}//千万别忘记pushdown操作,有可能之前做过lazy标记但还未更新 pushdown(id);//递归两个子节点 update_interval(id<<1,L,R,val);update_interval(id<<1|1,L,R,val);pushup(id);
}

区间查询操作比较简单,准确来说就是把目标区间分成若干个已经划分好的区间,这些区间都是无交集的,但这些区间并不一定在树的同一层,我们只需要返回这些区间的sum值即可,代码:

int query_interval(int id,int L,int R)
{if(l[id]>R||r[id]<L) return 0;if(l[id]>=L&&r[id]<=R)	return sum[id];pushdown(id);return query_interval(id<<1,L,R)+query_interval(id<<1|1,L,R);
}

上面就是线段树的基本代码了,大家可以按照我一开始放置的百度图片进行理解,线段树模板比较长,有时候也需要灵活转变,所以一定要深刻理解他的本质,在接下来的博客中我会放一些线段树的题目,如果大家有什么好的线段树题目,欢迎在评论区里面分享!


http://chatgpt.dhexx.cn/article/q0UJyBBW.shtml

相关文章

C++ 线段树

目录&#xff1a; 线段树基本常识 线段树的的存储 线段树的基本操作 1. 建树 2. 单点查询 3. 单点修改 4. 区间查询 5. 区间修改 例题推荐&#xff1a; 线段树 1 线段树 2 线段树基本常识 线段树是一种二叉搜索树&#xff08;即每个节点最多有两颗子树&#xff09;&…

线段树(java)

线段树描述&#xff1a; 线段树是一种二叉搜索树&#xff0c;与区间树相似&#xff0c;它将一个区间划分成一些单元区间&#xff0c;每个单元区间对应线段树中的一个叶结点。 使用线段树可以快速的查找某一个节点在若干条线段中出现的次数&#xff0c;时间复杂度为O(logN)。而未…

什么是线段树

线段树的概念 线段树是一种二叉搜索树&#xff0c;与区间树相似&#xff0c;它将一个区间划分成一些单元区间&#xff0c;每个单元区间对应线段树中的一个叶结点。 对于线段树中的每一个非叶子节点[a,b]&#xff0c;它的左儿子表示的区间为[a,(ab)/2]&#xff0c;右儿…

李超线段树

什么是李超线段树 先以一个问题引入&#xff1a; 在平面上有两种操作&#xff08;强制在线&#xff09;&#xff1a; 插入一条表达式为 L : y k*xb 的直线&#xff0c;给出 k ,b 。 给出 t&#xff0c;求当前所有直线中与直线 x t 交点的纵坐标最大是多少 直线取 max 应该是…

线段树详解 (原理,实现与应用)

线段树详解 By 岩之痕 目录&#xff1a; 一&#xff1a;综述 二&#xff1a;原理 三&#xff1a;递归实现 四&#xff1a;非递归原理 五&#xff1a;非递归实现 六&#xff1a;线段树解题模型 七&#xff1a;扫描线 八&#xff1a;可持久化 (主席树) 九&#xff1a;练习题 一&a…

线段树详解(附图解,模板及经典例题分析)

1.基本概念 ​ 线段树是一种常用来维护 区间信息 \color{Maroon}区间信息 区间信息 的数据结构 ​ 可以在 O(logn) 的时间复杂度内实现单点修改、区间修改、区间查询&#xff08;区间求和&#xff0c;求区间的最大值&#xff0c;求区间的最小值&#xff09;等操作 ​ 如下图…

线段树详解

文章目录 1. 什么是线段树2. 线段树的用处3. 线段树的模板题4. 动态开点线段树5. 权值线段树5. 可持久化线段树 1. 什么是线段树 顾名思义 &#xff0c; 线段树是一棵二叉树 &#xff0c; 但不同的是这棵树的结点储存的值是一个数列中 [ l , r ] [l,r] [l,r] 的某个需要的值 …

超详解线段树(浅显易懂,几乎涵盖所有线段树类型讲解,匠心之作,图文并茂)

一&#xff0c;什么是线段树&#xff1f; 线段树是怎样的树形结构? 线段树是一种二叉搜索树&#xff0c;而二叉搜索树&#xff0c;首先满足二叉树&#xff0c;即每个结点最多有两颗子树&#xff0c;并且是一颗搜索树&#xff0c;我们要知道&#xff0c;线段树的每个结点都存储…

线段树 从入门到进阶(超清晰,简单易懂)

目录 第一部 概念引入第二部 简单&#xff08;无pushdown&#xff09;的线段树1、单点修改&#xff0c;区间查询2、区间修改&#xff0c;单点查询 第三部 进阶线段树第四部 乘法&#xff08;根号&#xff09;线段树1、乘法线段树2、根号线段树模板题与代码&#xff1a;单点修改…

Navicat Premium 数据库开发工具

官网地址&#xff1a;https://www.navicat.com.cn/ Navicat Premium 是一套数据库开发工具&#xff0c;让你从单一应用程序中同时连接 MySQL、MariaDB、MongoDB、SQL Server、Oracle、PostgreSQL 和 SQLite 数据库。它与 Amazon RDS、Amazon Aurora、Amazon Redshift、Microso…

Navicat 一款数据库开发软件

Navicat premium是一款数据库管理工具&#xff0c;非常好用&#xff0c;能复制表结构等&#xff0c;大大简化了后端开发的工程量&#xff0c;其软件示意图如下 Navicat Premium 15 已经发布了&#xff0c;但是价格昂贵&#xff0c;所以将 no-money version 的双手奉上。 使用…

Java学习中的数据库和数据库开发工具

一、数据库 1、数据库&#xff0c;通常是一个戒一组文件&#xff0c;保存了一些符合特定规格的数据&#xff0c;数据库对应的英询单词是DataBase&#xff0c;简称DB&#xff1b;数据库软件称为数据库管理系统&#xff0c;英文简称DBMS&#xff0c;全称为DataBase Management S…

五大主流数据库深度对比!数据库开发、管理看这篇就够了

开发数据库应用&#xff0c;选择一个好的数据库是非常重要的。目前&#xff0c;商品化的数据库管理系统以关系型数据库为主导产品&#xff0c; 技术比较成熟。面向对象的数据库管理系统技术先进&#xff0c;数据库易于开发、维护。无论是关系型数据库还是非关系型数据库&#x…

mysql数据库开发环境_MySQL数据库教程-环境与集成开发工具

MySQL数据库基础教程 数据库是存储数据与管理数据的基础,也是目前绝大多数动态网站开发所必备的基础知识。MySQL数据库教程系列文章主要以MySQL数据库管理系统为例对关系型数据库相关定义与操作等进行讲解。并结合php与PDO等讲解,为初学者提供快速学习PHP动态网站开发所需的知…

C/C++ MySQL数据库开发

C/C++ MySQL数据库开发笔记 MySQL启动方式手动启动MySQL数据库:命令行方式启动MySQL数据库:配置环境变量MySQL在Linux下安装MySQL的配置文件重要参数设置C/C++ 访问MySQL数据库(MySQL开发环境的配置(MySQL开发头文件和库文件)MySQL 数据库的连接MySQL 数据类型以及对应的 …

1、 数据库开发规范

第1章 数据库开发规范的制定 数据库设计步骤&#xff1a; 1、数据结构设计&#xff1a;逻辑设计-》物理设计 2、实际工作中&#xff1a;逻辑设计物理设计 3、物理设计&#xff1a;表名字段名字段类型 数据库设计几个规范&#xff1a; 数据库命名规范、数据库基本设计规范…

数据库开发-2-开发数据库的要点

Lec2-开发数据库的要点 1. 开发成功数据库应用的特点 需要理解数据库体系结构需要理解锁和并发控制特性&#xff1a;每个数据库都以不同的方式实现最重要的是不要把数据库当"黑盒" 要了解每一个数据库的体系结构和特征最常见的问题就是因为我们对数据库本身了解不足…

数据库开发(Sqlite)

1、数据库开发 1.1 数据与数据管理 什么是信息&#xff1f;   信息是指对现实世界存在方式或运动状态的反应。 什么是数据&#xff1f;   数据是指存储在某一媒体上&#xff0c;能够被识别的物理符号&#xff1b;   数据的概念在数据处理领域已经被大为拓宽&#xff0c…

《软件测试的艺术》第4章:测试用例的设计-白盒测试

写在前面&#xff1a;原书中包含白盒测试、黑盒测试、错误猜测、测试策略四个小节&#xff0c;涵盖内容较多&#xff0c;因此按章节拆分叙述。 《软件测试的艺术》&#xff1a; 白盒测试--语句覆盖 语句覆盖的用例设计原则&#xff1a;将程序中的每条语句至少执行一次。 白盒…

《软件测试的艺术》读后感 Or 读书笔记

《软件测试的艺术》读后感 Or 读书笔记 第一章 一次自评价测试第二章 软件测试的心理学和经济学第三章 代码检查、走查与评审第四章 测试用例的设计第五章 模块&#xff08;单元&#xff09;测试第六章 更高级别的测试第七章 可用性&#xff08;或用户体验&#xff09;测试第八…