当前位置: 首页 > news >正文

线段树:高效区间操作的利器

线段树的基本概念

线段树(Segment Tree)是一种二叉树数据结构,用于高效处理区间查询和区间更新问题。其核心思想是将区间递归划分为若干子区间,每个节点存储对应区间的聚合信息(如求和、最大值、最小值等)。线段树常用于解决静态或动态区间查询问题,时间复杂度通常为 $O(\log n)$。

线段树的构建

线段树的构建基于递归分治。给定一个长度为 $n$ 的数组,根节点表示区间 $[0, n-1]$,每个非叶子节点的左右子节点分别表示区间的左半和右半部分。

关键步骤

  1. 初始化节点:每个节点存储区间 $[l, r]$ 的聚合值。
  2. 递归划分:若当前区间 $[l, r]$ 不为单点(即 $l \neq r$),则递归构建左右子树,分别处理 $[l, mid]$ 和 $[mid+1, r]$,其中 $mid = \lfloor (l+r)/2 \rfloor$。
  3. 合并信息:根据左右子树的聚合值计算当前节点的值(如求和时,父节点值为左右子节点值之和)。

示例代码

struct SegmentTreeNode { int l, r; int sum; // 以区间和为例 SegmentTreeNode *left, *right; SegmentTreeNode(int l, int r) : l(l), r(r), sum(0), left(nullptr), right(nullptr) {} }; SegmentTreeNode* build(int l, int r, vector<int>& nums) { SegmentTreeNode* node = new SegmentTreeNode(l, r); if (l == r) { node->sum = nums[l]; return node; } int mid = (l + r) / 2; node->left = build(l, mid, nums); node->right = build(mid + 1, r, nums); node->sum = node->left->sum + node->right->sum; return node; }

区间查询

线段树的查询通过递归实现,根据目标区间与当前节点区间的重叠情况分三种情况处理:

  1. 完全覆盖:当前节点区间完全包含于查询区间,直接返回节点值。
  2. 部分重叠:递归查询左右子树并合并结果。
  3. 无重叠:返回不影响结果的初始值(如求和时为0)。

示例代码

int query(SegmentTreeNode* node, int l, int r) { if (node->r < l || node->l > r) return 0; // 无重叠 if (l <= node->l && node->r <= r) return node->sum; // 完全覆盖 return query(node->left, l, r) + query(node->right, l, r); // 部分重叠 }

单点更新

单点更新需要递归定位到目标叶子节点,更新其值后回溯调整父节点的聚合值。

示例代码

void update(SegmentTreeNode* node, int index, int val) { if (node->l == node->r) { node->sum = val; return; } int mid = (node->l + node->r) / 2; if (index <= mid) update(node->left, index, val); else update(node->right, index, val); node->sum = node->left->sum + node->right->sum; }

区间更新与懒惰标记

区间更新若直接逐个单点更新效率较低,引入懒惰标记(Lazy Propagation)延迟更新操作。

  1. 标记下传:当需要访问子节点时,将当前节点的标记和未完成的更新操作下传给子节点。
  2. 标记应用:在查询或更新时检查并处理懒惰标记。

示例代码(带懒惰标记)

struct SegmentTreeNode { int l, r; int sum, lazy; // lazy存储未下传的增量 SegmentTreeNode *left, *right; SegmentTreeNode(int l, int r) : l(l), r(r), sum(0), lazy(0), left(nullptr), right(nullptr) {} }; void pushDown(SegmentTreeNode* node) { if (node->lazy != 0) { node->left->sum += node->lazy * (node->left->r - node->left->l + 1); node->right->sum += node->lazy * (node->right->r - node->right->l + 1); node->left->lazy += node->lazy; node->right->lazy += node->lazy; node->lazy = 0; } } void rangeUpdate(SegmentTreeNode* node, int l, int r, int val) { if (node->r < l || node->l > r) return; if (l <= node->l && node->r <= r) { node->sum += val * (node->r - node->l + 1); node->lazy += val; return; } pushDown(node); rangeUpdate(node->left, l, r, val); rangeUpdate(node->right, l, r, val); node->sum = node->left->sum + node->right->sum; }

应用场景

  1. 区间求和/最值:静态或动态数据的高效查询。
  2. 区间修改:批量增减、赋值等操作。
  3. 二维线段树:扩展至二维平面问题(如矩形区域查询)。

通过合理设计节点存储的聚合信息和懒惰标记逻辑,线段树可灵活适配多种问题需求。

http://www.jsqmd.com/news/505676/

相关文章:

  • PageHelper分页插件与民航电子数据库的兼容性实战:从报错到解决的全过程
  • 终极Steam创意工坊模组下载器WorkshopDL:跨平台免费获取游戏模组的完整指南
  • 5分钟终极指南:让Android Studio秒变中文开发环境的完整教程
  • 还在靠堆砌人力维持增长?AgentOffice实现跨量级增效香吗?
  • AudioSeal快速上手:AudioSeal Web界面多语言切换(中/英/日/韩)配置方法
  • 基于最大功率跟踪MPPT算法的直流侧电压稳定控制,光伏电池充电模型及双向电路充放电技术研究
  • Spring Boot -- 学习记录Day3
  • 设计与实现】基于STC12C5A60S2的智能鱼缸控制系统:温控、LED照明、投喂与水循环
  • ChatTTS最新模型解析:从架构设计到生产环境部署指南
  • 手把手教你解决labelimg安装后无法运行的问题(附常见错误排查)
  • 逆向工程实战:XXTEA算法解密与混淆处理
  • 3步极速汉化:让Android Studio告别语言障碍,提升开发效率
  • Blender新手必看:3种超简单模型环绕技巧(附常见问题解决)
  • 前端·小白也能看懂系列:3D魔方旋转相册
  • Blender3mfFormat技术方案实战:3D打印全流程解决方案
  • Navicat连接Oracle闪退?3步搞定OCI配置(附最新Instant Client下载)
  • WeChatExporter终极指南:三步完成iOS微信聊天记录完整备份与查看
  • 戴森球计划工厂蓝图库:从新手到专家的终极效率提升方案
  • 企业级CosyVoice语音方案部署:高可用架构与网络安全考量
  • SPH流体模拟实战:如何用哈希表优化邻域搜索(附完整C++代码)
  • 力扣第80题:划分字母区间
  • Oracle VM VirtualBox报错VERR_SUPDRV_NO_RAW_MODE?三分钟搞定ENSP和Docker的共存难题
  • 基于单片机的自动窗控制系统设计
  • 预防胜于治疗:给你的RStudio Server设置自动清理session,告别启动卡死
  • 蓝桥题目回顾2
  • CSDN-推荐开源项目-auto-x-to-wechat
  • 实战对比:OpenCV中RANSAC与最小二乘法在图像误匹配剔除中的性能差异
  • KOOK艺术馆入门指南:无需Python基础的Streamlit艺术画廊启动
  • HiC-Pro实战:从零到一构建上游数据处理环境
  • ComfyUI新手必看:如何用Easy-Use插件5分钟搞定你的第一个AI图像生成工作流