C++的数据结构(八):线段树

        线段树是一种高效的树形数据结构,用于处理区间查询和区间更新问题。它的基本思想是将一个大的区间分解为若干个小的、不相交的区间,每个小区间对应线段树中的一个节点。线段树的每个节点保存了该区间的信息(如区间最大值、区间和等),这使得我们能在对数时间内完成区间查询和更新操作。

        线段树的构建通常采用递归方式,自底向上逐层构建。每个节点的信息由其子节点的信息合并而来,如区间和节点的值即为其左右子区间节点值的和。如下图所示。

         构建线段树时,我们通常需要一个原始数组和一个递归函数。递归函数的作用是确定每个节点所代表的区间范围,并根据需要计算并保存节点的信息。代码如下。

struct Node {int start, end;int sum; // 假设我们存储的是区间和Node *left, *right;Node(int s, int e) : start(s), end(e), sum(0), left(nullptr), right(nullptr) {}
};Node* build(int arr[], int start, int end) {if (start > end) return nullptr;Node* root = new Node(start, end);if (start == end) {root->sum = arr[start];return root;}int mid = (start + end) / 2;root->left = build(arr, start, mid);root->right = build(arr, mid + 1, end);root->sum = root->left->sum + root->right->sum;return root;
}

        区间查询是线段树的一个重要应用,可以快速获取某个区间内的信息。查询时,我们根据查询区间的位置与线段树节点的区间关系,决定是递归查询左子树、右子树,还是将两者的信息合并。代码如下。

int query(Node* root, int qs, int qe) {if (root->start > qe || root->end < qs) return 0;if (qs <= root->start && qe >= root->end) return root->sum;int sum = 0;sum += query(root->left, qs, qe);sum += query(root->right, qs, qe);return sum;
}

        区间更新操作通常涉及对某个区间内的元素进行加减操作或其他更复杂的操作。区间更新需要同时维护一个懒惰标记(Lazy Propagation)数组,用于记录节点下尚未传导到子节点的更新信息。懒惰标记处理的代码通常较为复杂,示例为比较简单的代码。

void update(Node* root, int index, int value) {// 如果当前节点的区间不包含index,直接返回if (root->start > index || root->end < index) return;// 如果当前节点是一个叶子节点,直接更新值if (root->start == root->end) {root->sum = value;return;}// 递归更新左子树或右子树update(root->left, index, value);update(root->right, index, value);// 递归回溯时更新当前节点的值root->sum = root->left->sum + root->right->sum;
}

        下面是一个完整的例子,展示如何使用线段树解决区间和查询与更新的问题。代码如下。

#include <iostream>
#include <vector>
using namespace std;
struct Node {int start, end;int sum; // 假设我们存储的是区间和Node *left, *right;Node(int s, int e) : start(s), end(e), sum(0), left(nullptr), right(nullptr) {}
};Node* build(int arr[], int start, int end) {if (start > end) return nullptr;Node* root = new Node(start, end);if (start == end) {root->sum = arr[start];return root;}int mid = (start + end) / 2;root->left = build(arr, start, mid);root->right = build(arr, mid + 1, end);root->sum = root->left->sum + root->right->sum;return root;
}
int query(Node* root, int qs, int qe) {if (root->start > qe || root->end < qs) return 0;if (qs <= root->start && qe >= root->end) return root->sum;int sum = 0;sum += query(root->left, qs, qe);sum += query(root->right, qs, qe);return sum;
}void update(Node* root, int index, int value) {// 如果当前节点的区间不包含index,直接返回if (root->start > index || root->end < index) return;// 如果当前节点是一个叶子节点,直接更新值if (root->start == root->end) {root->sum = value;return;}// 递归更新左子树或右子树update(root->left, index, value);update(root->right, index, value);// 递归回溯时更新当前节点的值root->sum = root->left->sum + root->right->sum;
}int main(){int arr[] = {1, 3, 5, 7, 9, 11};int n = sizeof(arr) / sizeof(arr[0]);Node* root = build(arr, 0, n - 1);// 查询区间 [1, 4] 的和cout << "Sum from index 1 to 4: " << query(root, 1, 4) << endl; // 更新 update(root, 2, 10);// 再次查询区间 [1, 4] 的和,应该会反映更新cout << "Sum from index 1 to 4 after update: " << query(root, 1, 4) << endl; return 0;
}

        结果如下图所示。

         

        在实际应用中,根据具体问题的不同,线段树还可以存储更多的信息,并扩展出更多的功能,如最大值查询、区间乘法等。此外,实现线段树时还需要特别注意内存管理,避免内存泄漏。

        通过上述例子,我们完整地展示了线段树的基本原理、基本操作以及一个实际应用的例子。这只是一个开始,线段树是一个功能强大的数据结构,在实际问题中有着非常广泛的应用。

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

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

相关文章

尝试使用官方jailhouse-images仓库运行jailhouse

jailhouse 的官方 demo 演示仓库 Demo and testing images: https://github.com/siemens/jailhouse-images 通过jailhouse 的官方 demo 演示仓库&#xff0c;可以直接编译出带有部署有jailhouse程序的Linux镜像&#xff0c;有多个目标平台的Linux镜像可选&#xff0c;也有在qe…

C语言实训项目源码-02餐厅饭卡管理系统-C语言实训C语言大作业小项目

C语言餐厅饭卡管理系统 一、主要功能 主要功能模块 页面名称 实现功能 负责人 进入页面 进入程序 主函数 系统主要功能 修改密码函数 修改密码 充值&#xff0c;显示函数 饭卡充值与信息显示 购买饭菜…

SpringBoot:缓存

点击查看SpringBoot缓存demo&#xff1a;LearnSpringBoot09Cache-Redis 技术摘要 注解版的 mybatisCacheConfigCacheableCachePut&#xff1a;既调用方法&#xff0c;又更新缓存数据&#xff1b;同步更新缓存CacheEvict&#xff1a;缓存清除Caching&#xff1a;定义复杂的缓存…

Python-VBA函数之旅-vars函数

目录 一、vars函数的常见应用场景 二、vars函数使用注意事项 三、如何用好vars函数&#xff1f; 1、vars函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a;https://myelsa1024.blog.csdn.net/ 一、vars函数…

业务系统加固和安全设备加固

业务系统加固 业务系统包含哪些系统? 业务系统漏洞面临的风险 1web风险 2漏洞扫描&#xff0c;端口扫描 3系统漏洞 4逻辑漏洞 5 信息泄露 6拒绝服务 7口令爆破 加固方式&#xff1a; 在风险加上修复 1web漏洞&#xff1a; 包括csrf,xss&#xff0c;口令破解等等 修…

走进Java接口测试之多数据源切换示例

文章目录 一、前言二、demo实现2.1、开发环境2.2、构建项目2.3、配置数据源2.4、编写配置文件2.5、编写Dao层的mapper2.6、编写实体成层2.7、编写测试类2.8、验证结果 三、多数据源 demo 实现3.1、配置数据源3.2、增加pom文件3.3、修改数据源读取方式&#xff1a;3.4、增加动态…

图片转base64【Vue + 纯Html】

1.template <el-form-item label"图片"><div class"image-upload-container"><input type"file" id"imageUpload" class"image-upload" change"convertToBase64" /><label for"imageU…

如何配置测试环境?(非常详细)零基础入门到精通,收藏这一篇就够了

测试环境配置是一个关键的步骤&#xff0c;用于确保软件在开发过程中能够得到全面的测试&#xff0c;以提高软件的质量、性能和安全性。 测试环境配置的详细步骤&#xff1a; **确定测试环境需求&#xff1a;**在开始测试环境搭建之前&#xff0c;首先需要明确测试环境的需求…

Markdown 高级表格控制 ∈ Markdown 使用笔记

文章目录 Part.I IntroductionPart.II 表格样式控制Chap.I 对齐方式Chap.II 表格中文本控制Chap.III 单元格合并Chap.IV 颜色控制 Part.III 实用技巧Chap.I Excel 转 HTML Reference Part.I Introduction 本文是 Markdown 使用笔记 的子博客&#xff0c;将介绍如何优雅地使用 …

【Redis】Redis 主从集群(二)

1.哨兵机制原理 1.1.三个定时任务 Sentinel 维护着三个定时任务以监测 Redis 节点及其它 Sentinel 节点的状态 1&#xff09;info 任务&#xff1a;每个 Sentinel 节点每 10 秒就会向 Redis 集群中的每个节点发送 info 命令&#xff0c;以获得最新的 Redis 拓扑结构 2&#xff…

HOJ 修改首页 和后端logo图片 网页收藏标识ico 小白也会的方法

HOJ 是一款优雅知性的在线评测系统&#xff0c;像一位温文尔雅的女性&#xff0c;你会慢慢喜欢上她的。 制作图片素材 用图像编辑软件 比如 **光影魔术手4.0** 制作以下素材 logo.a0924d7d.png 为前台导航栏左边的logo&#xff0c; 600*200 backstage.8bce8c6e.png 为后台侧…

2024 年 4 月公链研报:比特币减半、市场回调以及关键进展

作者&#xff1a;stellafootprint.network 数据来源&#xff1a;Footprint Analytics 公链研究页面 四月&#xff0c;加密市场在经济环境变化中取得了重要进展。4 月 20 日的比特币完成减半&#xff0c;但市场整体低迷导致比特币及前 25 大公链加密货币价格下跌。与此同时&am…