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

news2024/9/25 21:27:25

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

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

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

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.coloradmin.cn/o/1677413.html

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

相关文章

Spring Boot实现多数据源快速入门

1.为什么需要多数据源&#xff1f; 多数据源既动态数据源&#xff0c;项目开发逐渐扩大&#xff0c;单个数据源、单一数据源已经无法满足需求项目的支撑需求。本文采用dynamic-datasource-spring-boot-starter实现多数据源&#xff0c; 主要特性 支持 数据源分组 &#xff0…

Ansible自动化运维中的User用户管理模块应用详解

作者主页&#xff1a;点击&#xff01; Ansible专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年5月14日14点12分 在Ansible中&#xff0c;user 模块主要用于管理系统用户账户。它可以创建、修改、删除用户&#xff0c;并管理用户的属性&#xff0c;比如密码、…

RK3576 Camera:资源介绍

RK3576是RK今年上市的中高端旗舰芯片&#xff0c;定位弱于RK3588。这篇文章主要分享一下RK3576这颗主控芯片的camera资源。 &#xff08;1&#xff09;RK3576 camera资源 ①RK3576 camera硬件框图 RK3576的camera硬件框图如图所示&#xff0c;拥有一路4lane的DCPHY&#xff…

中关村论坛 | 区块链与隐私计算论坛倒计时1天!

「区块链与隐私计算论坛」 倒计时1天&#xff01; 地址&#xff1a;中关村国家自主创新示范区会议中心&#xff08;新建宫门路2号&#xff09;万春厅 时间&#xff1a;2024年4月27日&#xff0c;下午14:30-17:00 本次论坛围绕释放数据要素价值深入探讨如何将区块链与隐私计算…

六西格玛培训证书攻略2024:一站式解决方案助你快速上手

目前&#xff0c;企业对于员工的专业能力和综合素质要求越来越高。六西格玛作为一种先进的质量管理方法&#xff0c;已经成为众多企业提升运营效率、降低成本的重要手段。张驰咨询针对2024年六西格玛培训证书考取&#xff0c;为广大学员制定了实用的攻略&#xff0c;帮助学员们…

【SRC实战】文件名回显导致反射型XSS,URL重定向

挖个洞先 https://mp.weixin.qq.com/s/hnrm-snkETuR-gqPOSnQXQ “ 以下漏洞均为实验靶场&#xff0c;如有雷同&#xff0c;纯属巧合 ” 01 — 漏洞证明 一、反射型XSS “ 文件名回显&#xff0c;能否触发XSS&#xff1f;” 1、灯塔扫到敏感文件&#xff0c;发现1.txt会在…

使用apifox连接WebSocket请求,重发消息

背景&#xff1a;在 在线文档中&#xff0c;通常使用的都是WebSocket请求&#xff0c;就会导致F12抓包不是通常的http/https 在浏览器中查看WebSocket的方法 打开F12&#xff0c;切换到网络&#xff0c;选择过滤WebSocket请求&#xff0c;刷新页面 连接WebSocket请求的方法 …

python 脚本压缩文件linux 正常,windows 文件夹/文件名称 被加上了上级文件夹名

场景&#xff1a; php 在调用python 脚本&#xff0c;进行文件压缩&#xff08;因为php的压缩大文件总是超时&#xff09;&#xff0c;linux/mac 环境文件/文件夹名压缩前后一致&#xff0c;windows 压缩后 文件/文件夹名被改变为 上级 文件夹原名 原因&#xff1a; window…

国标GB28181协议EasyCVR视频汇聚平台获取设备录像仅展示部分片段的原因排查

国标GB28181协议EasyCVR安防平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力&#xff0c;平台支持7*24小时实时高清视频监控&#xff0c;能同时播放多路监控视频流&#xf…

umi项目运行时配置,app.ts

运行时配置和配置的区别是他跑在浏览器端&#xff0c;基于此&#xff0c;我们可以在这里写函数、tsx、import 浏览器端依赖等等&#xff0c;注意不要引入 node 依赖。 getInitialState 用于获取初始化数据&#xff0c;初始化数据使用 useModel 在各个组件中使用&#xff0c;在…

信创替代后的设备处置

信创替代后的设备处置 在信创项目中替换下来的设备&#xff0c;如果从技术层面讲还具有较高的应用价值&#xff0c;如何处置呢&#xff1f; 一、数据处置 信创适配完成后&#xff0c;这些被替换下来的服务器上有大量的数据&#xff08;包括结构化和非结构化&#xff09;&…

打造动态图文展示,电子翻书一体机成展厅新宠!

随着多媒体技术的飞速演进&#xff0c;传统的静态展示方式已逐渐无法满足现代社会对于创新的渴望&#xff0c;为了跟上这一步伐&#xff0c;展厅中的展览形式也在不断地迭代&#xff0c;其中&#xff0c;电子翻书一体机以其独特的魅力&#xff0c;成为了备受瞩目的新型展项&…

JAVA实验项目(一):JAVA面向对象特征性实验

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

2024CCPC郑州邀请赛暨河南省赛

比赛记录&#xff1a;看群里大家嘎嘎拿牌&#xff0c;自己个人来solo了一下&#xff0c;发现简单到中等题很多&#xff0c;写了两小时出了7题&#xff0c;但是写的比较慢&#xff0c;对难题把握还是不准确 补题 &#xff1a; A题确实巧妙充分利用题目的数据范围来思考问题&…

Python邮件处理库之flanker使用详解

概要 Flanker是一个开源的邮件处理库,专门设计用于解析、验证和构建电子邮件地址和MIME消息。由Mailgun开发,它旨在提高邮件处理的效率和准确性,尤其适用于需要高效邮件验证和解析的应用程序。 安装 安装Flanker非常简单,可以通过Python的包管理器pip进行安装: pip ins…

GRE over IPsec VPN实验

一、拓扑图 二、组网需求 某企业总部、分支1、分支2分别通过 R1&#xff0c;R3&#xff0c;R4 接入互联网&#xff0c;配置默认路由连通公网按照图示配置 IP 地址&#xff0c;R1&#xff0c;R3&#xff0c;R4 分别配置 Loopback0 口匹配感兴趣流&#xff0c;Loopback1 口模拟业…

微软推出的Microsoft Fabric 到底是什么?

近期&#xff0c;总有客户问小编&#xff0c;微软推出的 Microsoft Fabric 是什么&#xff1f;这个产品有什么特别之处呢&#xff1f;希望下面这篇文章能为大家解开一些疑惑。 微软Fabric是2023年5月推出的一个数据分析平台&#xff0c;它将关键数据管理和分析工作负载整合到一…

Docker 使用 CentOS 镜像

使用 docker run 直接运行 CentOS 7 镜像&#xff0c;并登录 bash。 C:\Users\yhu>docker run -it centos:centos7 bash Unable to find image centos:centos7 locally centos7: Pulling from library/centos 2d473b07cdd5: Pull complete Digest: sha256:be65f488b7764ad36…

力扣HOT100 - 139. 单词拆分

解题思路&#xff1a; 动态规划 class Solution {public boolean wordBreak(String s, List<String> wordDict) {Set<String> wordDictSet new HashSet(wordDict);boolean[] dp new boolean[s.length() 1];dp[0] true;for (int i 1; i < s.length(); i) {…

数图智能营运管理系统助力企业数字化转型升级

数图智能营运管理系统不仅仅是一个业绩查看工具&#xff0c;它还具备了主动预警机制以及专家级的品类分析逻辑。系统能够协助企业持续优化库存管理&#xff0c;提升品类结构合理性&#xff0c;显著提高运营效率&#xff0c;减少对员工专业技能的依赖&#xff0c;并缩短处理时间…