线段树入门 - idle

news/2025/10/29 20:05:11/文章来源:https://www.cnblogs.com/idle-onlooker/p/19173592

前言

笔者从2025.4.22第一次通过线段树模板,至今也不过半年时间,虽然短暂,但是却让其成为了笔者最喜欢的算法,因此,我常常会大喊我是线段树的狗。为了帮助自己记忆以及造福后人,笔者提键盘写出了这篇文章。——2025.10.29

为什么要学线段树

我认为线段树是世界上最好用的数据结构,没有之一!!!
当然,我直接这么说你是肯定不信的,让我们看看线段树都能干什么。
对于一颗线段树,它能支持:在单次时间复杂度为 \(O(\log n)\) 的情况下进行区间修改,查询。

Q:没了?
A:对,没了。

那我学个蛋,跑路了。
等等先别走,那我问你,你暴力对一个数组进行区间修改的最坏时间复杂度是不是 \(O(n)\) 的?
你说是?但是线段树可以 \(O(\log n)\) 啊,这难道真的不值得你学一下吗?
你说不值得?
我**%#*#%#
算了,闲话少说,让我们进入正题。

关于线段树的介绍

线段树长什么样?
长这样

这张图是什么意思呢?
每个方格代表一个线段树的节点。
每个节点上面的 \(id\) 代表这个点的编号。
而节点中写的 \([L,R]\) 则代表这个节点维护下标范围在 \(L\sim R\) 的区间。
例如编号为 \(5\) 的节点维护的是下标为 \(4\sim 5\) 的区间。
由观察可以得到,对于一个节点,如果它维护的区间 \([L,R]\) 满足 \(L\ne R\) 那么它一定会有两个儿子节点(我们将其称作左儿子和右儿子),如果这个点的编号为 \(id\),那么它的左儿子的编号为 \(id\times 2\),右儿子的编号为 \(id\times 2+1\)
它的左儿子维护的区间为 \([L,\lfloor \frac{L+R}{2}\rfloor]\)
它的右儿子维护的区间为 \([\lfloor \frac{L+R}{2}\rfloor+1,R]\)
知道这些基础概念之后我们就可以尝试实现一颗线段树了。
*注:下文中的 \(L,R\) 均代表当前线段树节点维护的区间的左右端点,\(mid\) 均代表 \(\lfloor \frac{L+R}{2}\rfloor\)\(l,r\) 代表查询区间(此限制对代码内的变量仍然适用)。

线段树的简单实现

我们首先要建树,现在假设我们维护长度为 \(n\) 的数组 \(a\) 的区间和。

建树

void make_tree(int L,int R,int id){if(L==R){//到达叶子结点,没有左右儿子tree[id].sum=a[L];//直接赋值return ;//退出建树函数}int mid=(L+R)>>1;make_tree(L,mid,id<<1);//递归左儿子make_tree(mid+1,R,id<<1|1);//递归右儿子tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;//当前节点维护的区间和使用左右儿子来得到。
}

区间查询

还是刚才的图片

假设现在我们要查询区间 \([2,7]\)
那我们的答案就应用 \(id={17,9,5,12}\) 得出。
怎么实现?
我们先给代码,根据代码里面的注释进行理解。

int query(int l,int r,int L,int R,int id){if(l<=L&&r>=R) return tree[id].sum;//如果当前节点区间完全被查询区间包含,直接返回int mid=(L+R)>>1,ans=0;if(l<=mid) ans+=query(l,r,L,mid,id<<1);//如果当前区间有一部分落在在左儿子,那么递归左儿子,并将答案增加 if(r>mid/*写成r>=mid+1也可以*/) ans+=query(l,r,mid+1,R,id<<1|1);//如果当前区间有一部分落在在右儿子,那么递归右儿子,并将答案增加 return ans;//返回答案 
}

现在问题来了,如何证明这个函数是 \(O(\log n)\) 的,相信大多数初学者甚至已经学会线段树较长时间的人都不能给出一个比较完整的证明。
笔者在这里给出一个自己推出的证明方式。
首先要明确的是一颗线段树的深度是 \(\log n\) 的。
深度每增加一层,维护的区间长度会除以 \(2\)
这个很好理解。
假设查询区间为 \([l,r]\)
显然我们第一次调用函数一定会先访问节点编号为 \(1\) 的节点。
我们进行分类讨论:

  • 情况 \(1\)

\(l=1\)
如果 \(r>mid\) 那么会同时递归左右儿子,而左儿子由于被完全包含会直接在被访问时 \(return\) 掉,而递归右儿子时又会面对查询区间的右端点小于等于当前节点维护的右端点的情况,于是这个节点就会面对和它的父亲相同的情况。
如果 \(r<=mid\) 则只会递归左儿子,之后的情况就是 \(r>mid\) 的简易版了。
此时线段树的每层最坏会有两个节点被访问,时间复杂度 \(O(2\times\log n)\)

  • 情况 \(2\)

\(r=n\)
基本同上,不解释。

  • 情况 \(3\)

\(l\le mid\le r\)
我们会递归左右儿子,此时左儿子会退化成情况 \(2\),右儿子会退化成情况 \(1\)
线段树的每层最坏会有四个节点被访问,时间复杂度 \(O(4\times\log n)\)

  • 情况 \(4\)

\(l\le r\le mid\)
递归左儿子,在多次递归后迟早会退化成情况 \(1\) 或情况 \(2\) 或情况 \(3\)
线段树的每层最坏依旧会有四个节点被访问,时间复杂度 \(O(4\times\log n)\)

  • 情况 \(5\)

\(mid<l\le r\)
基本同上,不解释。
证毕。
可能写的比较抽象,但是没关系,其实你只需要知道线段树是单次查询 \(O(\log n)\) 的即可(对,其实你不明白也问题不大,但是我还是写了,也是因为笔者也因这个问题困惑了一段时间)。
区间查询就先到这里,现在就要讲修改了。

单点修改

其实学会查询后修改就没什么好说的了,直接上代码。

void add(int l,int k,int L,int R,int id){if(L==R){//到达修改的点tree[id].sum+=k; return ;}int mid=(L+R)>>1;if(l<=mid) add(l,k,L,mid,id<<1);//修改的点在左儿子 else add(l,k,mid+1,R,id<<1|1);//否则一定在右子树 tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;//不要忘记用儿子更新自己 
}

这个代码的时间复杂度很好证明,线段树的每层一定会有一个节点被访问,时间复杂度 \(O(\log n)\)
现在我们就可以 \(AC\) P3374 【模板】树状数组 1了。
但是为什么是单点修改,不是区间修改吗,退钱!!!

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

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

相关文章

2025年10月临江鳝丝店推荐:五家口碑店铺综合对比排行

临江鳝丝作为乐山地区的特色美食,近年来受到越来越多食客的关注。在选择临江鳝丝店时,消费者通常会考虑店铺的地域特色、食材新鲜度、烹饪技艺以及就餐环境等多个因素。根据餐饮行业数据显示,乐山地区特色餐饮门店数…

文档抽取技术在智能合同对比系统中的应用与优势分析

在商业活动日益频繁的今天,合同作为规范交易、界定权责的核心载体,其审查与管理的重要性不言而喻。传统的合同对比方式主要依赖于法务或业务人员的人工逐字阅读,耗时耗力且容易因疲劳或疏忽导致关键差异被遗漏。随着…

2025年10月临江鳝丝店对比报告:详析五家店铺特色与差异

临江鳝丝作为乐山地区的特色美食,近年来受到越来越多食客的关注。许多游客和本地居民在寻找正宗临江鳝丝店时,常常面临选择困难。根据餐饮行业数据显示,乐山地区主打临江鳝丝的店铺数量在2024年已达到数十家,但品质…

vs2022(2026)离线安装失败的问题解决

安装微软网站上的 创建 Visual Studio 的脱机安装包以进行本地安装 创建离线安装目录后,在内网机器安装一直出错。如果使用--noweb参数,就会提示要下载文件才能安装,但是内网机是不联网的,无法下载。 忘了截图,往…

家训

饿就吃饭菜成什么样了 2156 Div.2 D 压线过了。 考虑从低位到高位判定 0/1,每次 check 的数大约减半,那么 \(time=n+\frac{n}{2}+\frac{n}{4}+\dots=2n+\epsilon\),有 \(\epsilon\) 是因为可能上一步只删了下取整个…

2025年10月临江鳝丝店推荐榜:五家口碑店铺深度对比与选择指南

作为乐山地区特色美食的代表,临江鳝丝以其独特的烹饪工艺和鲜明的地方风味吸引着众多食客。选择一家正宗的临江鳝丝店不仅关乎味蕾享受,更关系到对传统美食文化的体验。当前餐饮市场呈现多元化发展趋势,消费者在选择…

VisionPro学习笔记-CogFixtureTool

CogFixtureTool 定位工具文档 1. 工具概述 CogFixtureTool 是康耐视 VisionPro 视觉软件中的核心坐标系管理工具,主要用于在图像中创建和管理坐标系转换。其核心功能是根据提供的二维变换关系,在图像的原始坐标系(U…

2025年10月临江鳝丝店推荐榜单:五家特色店铺详细对比分析

作为乐山地区最具代表性的江湖菜之一,临江鳝丝近年来受到越来越多美食爱好者的关注。根据餐饮行业数据显示,2025年临江鳝丝类餐厅的搜索量同比增长显著,反映出消费者对这道传统美食的持续热情。许多游客来到乐山,除…

2025年10月临江鳝丝店推荐:乐山地区五家优质店铺榜单与对比分析

对于想要品尝正宗临江鳝丝的朋友来说,选择一家品质可靠的店铺尤为重要。临江鳝丝作为乐山地区的特色美食,以其独特的烹饪技法和鲜明的地方风味受到广泛关注。当前餐饮市场注重食材新鲜度和工艺传承性,消费者在选择时…

2025年10月临江鳝丝店详细评测:结合实地体验与行业标准

作为乐山地区独具特色的美食代表,临江鳝丝近年来受到越来越多食客的关注。选择一家正宗的临江鳝丝店,不仅是为了满足味蕾的享受,更是对地方饮食文化的体验。许多游客和本地居民在寻找鳝丝店时,常常面临信息不全面、…

2025年10月临江鳝丝店评价榜:传统与创新菜系全面解析

临江鳝丝作为乐山地区的传统特色美食,近年来受到越来越多美食爱好者的关注。许多游客前往乐山旅游时,会将品尝正宗临江鳝丝列为重要行程;本地居民也时常需要寻找合适的餐厅招待亲友。在选择临江鳝丝店时,消费者通常…

25岁零基础转行软件测试挑战高薪,真的可以么?

我自己也是一个零基础转行过来的,听我的,如果你对现在的职业不感兴趣,又没多少钱,真的可以转行学软件测试!我可以摸着我的良心和过万的薪资单告诉你,学会了只赚不亏! 一、软件测试行业的前景如何?毫不夸张的说…

提高组模拟赛 40 A. 子序列 题解

提高组模拟赛 40 A. 子序列 题解 t1 笑传之 crash crash 标 题意略 首先有一个性质 对于所有由 \(c\) 个 \(0\),\(d\) 个 \(1\) 组成的任意串,他对答案的贡献是相等的。 我不会证,但是 gpt 真好用:证明🧮 证明思…

详细介绍:Hadoop

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

【题解】Educational Codeforces Round 105E

题目链接 Educational Codeforces Round 105E 题目大意 给定一张图,有三种操作:在 \(u\) \(v\) 之间连一条标号为 \(c\) 的边。 去掉 \(u\) \(v\) 之间的边。 询问是否有经过 \(k\) 个点的路径,使得可以从 \(v_1\) 走…

业务人员能学低代码吗

市场部想搭个活动报名系统,等 IT 排期要半个月;人事部想做个考勤统计表,Excel 公式总出错 —— 很多业务人员都有过 “要是自己能做系统就好了” 的想法,但一听到 “低代码”,又会犯怵:“我连代码都不会写,能学…

低代码只能做简单表单?复杂业务场景的适配方案

“低代码做个报名表单、请假审批还行,要做生产排程、供应链管理这种复杂系统,肯定不行吧?”—— 这是很多制造、零售企业的顾虑。 确实,复杂业务不像简单表单那样 “填信息、存数据” 就行,比如生产排程要考虑 “…

ARC183 做题记

训A () 题意 题解 \(\bf{record}\) B () 题意 题解 \(\bf{record}\) C () 题意 题解 \(\bf{record}\) D () 题意 题解 \(\bf{record}\)

C++小白修仙记_LeetCode刷题_459重复的子字符串

459. 重复的子字符串 给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。 示例: 输入: s = "abab" 输出: true 解释: 可由子串 "ab" 重复两次构成。 解法: class Solution …