线段树与扫描线 —— 详解算法思想及其C++实现

目录

一、线段树(Segment Tree)

基本概念

结构

操作

示例代码

二、扫描线(Sweep Line)

基本概念

应用场景

示例代码(矩形面积并集)

三、总结


一、线段树(Segment Tree)

基本概念

        线段树是一种用于处理区间查询和更新操作的数据结构,常用于解决区间最值、区间和等问题。它将一个区间划分为若干个子区间,每个子区间对应树中的一个节点。

结构

  • 叶子节点:代表数组中的单个元素。

  • 内部节点:代表数组中的某个区间,通常是其子节点的并集。

操作

  1. 构建(Build):从数组构建线段树,时间复杂度为 O(n)O(n)。

  2. 查询(Query):查询某个区间的信息(如最小值、最大值、和等),时间复杂度为 O(log⁡n)O(logn)。

  3. 更新(Update):更新数组中的某个元素,并相应地更新线段树,时间复杂度为 O(log⁡n)O(logn)。

示例代码

#include <vector>  // 引入向量容器,用于动态数组
#include <algorithm>  // 引入算法库,提供常用算法(如排序)class SegmentTree {
private:std::vector<int> tree;  // 线段树的数组表示int n;  // 原始数组的大小// 构建线段树的递归函数void build(const std::vector<int>& arr, int node, int start, int end) {if (start == end) {  // 如果当前区间只有一个元素tree[node] = arr[start];  // 直接将数组值赋给叶子节点} else {int mid = (start + end) / 2;  // 计算区间的中点build(arr, 2 * node + 1, start, mid);  // 递归构建左子树build(arr, 2 * node + 2, mid + 1, end);  // 递归构建右子树tree[node] = tree[2 * node + 1] + tree[2 * node + 2];  // 当前节点的值为左右子节点的和}}// 查询区间和的递归函数int query(int node, int start, int end, int l, int r) {if (r < start || end < l) {  // 如果查询区间与当前区间无交集return 0;  // 返回0,表示无贡献}if (l <= start && end <= r) {  // 如果当前区间完全包含在查询区间内return tree[node];  // 返回当前节点的值}int mid = (start + end) / 2;  // 计算区间的中点// 递归查询左右子树,并将结果相加return query(2 * node + 1, start, mid, l, r) + query(2 * node + 2, mid + 1, end, l, r);}// 更新数组中某个元素的递归函数void update(int node, int start, int end, int idx, int val) {if (start == end) {  // 如果当前区间只有一个元素tree[node] = val;  // 直接更新叶子节点的值} else {int mid = (start + end) / 2;  // 计算区间的中点if (idx <= mid) {  // 如果目标位置在左子树update(2 * node + 1, start, mid, idx, val);  // 递归更新左子树} else {  // 如果目标位置在右子树update(2 * node + 2, mid + 1, end, idx, val);  // 递归更新右子树}tree[node] = tree[2 * node + 1] + tree[2 * node + 2];  // 更新当前节点的值为左右子节点的和}}public:// 构造函数,初始化线段树SegmentTree(const std::vector<int>& arr) {n = arr.size();  // 获取原始数组的大小tree.resize(4 * n);  // 为线段树分配空间,通常为4倍数组大小build(arr, 0, 0, n - 1);  // 构建线段树}// 查询区间和的公共接口int query(int l, int r) {return query(0, 0, n - 1, l, r);  // 调用私有查询函数}// 更新数组中某个元素的公共接口void update(int idx, int val) {update(0, 0, n - 1, idx, val);  // 调用私有更新函数}
};

二、扫描线(Sweep Line)

基本概念

        扫描线算法是一种用于处理几何问题的算法,通常用于计算平面中多个矩形的并集面积、线段交点等问题。其核心思想是通过一条虚拟的线(通常是垂直于x轴或y轴的线)在平面上扫描,并在扫描过程中处理与线相交的几何对象。

应用场景

  • 矩形面积并集:计算多个矩形的总面积。

  • 线段交点:找出所有线段的交点。

  • 多边形面积:计算多边形的面积。

示例代码(矩形面积并集)

#include <vector>  // 引入向量容器,用于动态数组
#include <algorithm>  // 引入算法库,提供常用算法(如排序)
#include <set>  // 引入集合容器,用于存储和管理活动的区间// 定义事件结构体,表示矩形的开始或结束事件
struct Event {int x, y1, y2;  // x坐标,y1和y2表示矩形的垂直区间bool isStart;  // 标记事件是矩形的开始还是结束Event(int x, int y1, int y2, bool isStart) : x(x), y1(y1), y2(y2), isStart(isStart) {}
};// 比较函数,用于对事件按x坐标排序
bool compareEvents(const Event& a, const Event& b) {return a.x < b.x;  // 按x坐标升序排列
}// 计算矩形面积并集的函数
int calculateArea(const std::vector<std::vector<int>>& rectangles) {std::vector<Event> events;  // 存储所有事件(矩形的开始和结束)for (const auto& rect : rectangles) {// 添加矩形的开始事件(左边界)events.emplace_back(rect[0], rect[1], rect[3], true);// 添加矩形的结束事件(右边界)events.emplace_back(rect[2], rect[1], rect[3], false);}// 对所有事件按x坐标排序std::sort(events.begin(), events.end(), compareEvents);std::multiset<std::pair<int, int>> activeIntervals;  // 存储当前活动的垂直区间int prevX = 0;  // 前一个事件的x坐标int totalArea = 0;  // 总面积// 遍历所有事件for (const auto& event : events) {int currentX = event.x;  // 当前事件的x坐标if (!activeIntervals.empty()) {  // 如果有活动的区间int height = 0;  // 当前扫描线覆盖的总高度int prevY = -1;  // 前一个区间的y坐标// 遍历所有活动区间,计算覆盖的高度for (const auto& interval : activeIntervals) {if (prevY == -1) {  // 如果是第一个区间prevY = interval.first;  // 初始化prevY}if (interval.first > prevY) {  // 如果当前区间与前一个区间不重叠height += interval.first - prevY;  // 累加高度}prevY = std::max(prevY, interval.second);  // 更新prevY}// 计算当前扫描线与前一个扫描线之间的面积,并累加到总面积totalArea += height * (currentX - prevX);}prevX = currentX;  // 更新prevXif (event.isStart) {  // 如果是矩形的开始事件activeIntervals.insert({event.y1, event.y2});  // 将区间加入活动集合} else {  // 如果是矩形的结束事件activeIntervals.erase(activeIntervals.find({event.y1, event.y2}));  // 将区间从活动集合中移除}}return totalArea;  // 返回总面积
}

三、总结

  • 线段树:适用于区间查询和更新操作,常用于处理动态区间问题。

  • 扫描线:适用于处理几何问题,特别是与平面中的矩形、线段等相关的问题。

        这两种算法思想在解决特定类型的问题时非常有效,理解它们的原理和应用场景有助于在编程竞赛和实际开发中更好地解决问题。

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

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

相关文章

汇编代码中嵌入回调函数的优化说明

一、概述 在 PowerPC 的汇编代码中&#xff0c;我们需要实现调用 C 函数&#xff08;例如回调函数&#xff09;&#xff0c;并传递参数。本文将详细介绍如何通过一系列步骤完成这一目标&#xff0c;包括代码示例和详细的注释。 二、调用 C 函数的基本步骤及代码 1. 保存工作寄…

Uni-App 双栏联动滚动组件开发详解 (电梯导航)

本文基于提供的代码实现一个左右联动的滚动组件&#xff0c;以下是详细的代码解析与实现原理说明&#xff1a; <!--双栏联动滚动组件 - 技术解析功能特性&#xff1a;1. 左侧导航栏与右侧内容区双向联动2. 自适应容器高度3. 平滑滚动定位4. 动态内容位置计算 --> <te…

软考复习-传输介质与编码

传输介质 双绞线 传输距离100一200m&#xff0c;即网线&#xff0c;有多种分类 UTP非屏蔽双绞线 STP屏蔽双绞线 线序标准有两种为&#xff1a; T568A标准&#xff1a;绿白、绿、橙白、蓝、蓝白、橙、棕白、棕 T568B标准&#xff1a;橙白、橙、绿白、蓝、蓝白、绿、棕白、…

论文阅读笔记:Denoising Diffusion Probabilistic Models (3)

论文阅读笔记&#xff1a;Denoising Diffusion Probabilistic Models (1) 论文阅读笔记&#xff1a;Denoising Diffusion Probabilistic Models (2) 论文阅读笔记&#xff1a;Denoising Diffusion Probabilistic Models (3) 4、损失函数逐项分析 可以看出 L L L总共分为了3项…

PyTorch 面试题及参考答案(精选100道)

目录 PyTorch 的动态计算图与 TensorFlow 的静态计算图有何区别?动态图的优势是什么? 解释张量(Tensor)与 NumPy 数组的异同,为何 PyTorch 选择张量作为核心数据结构? 什么是 torch.autograd 模块?它在反向传播中的作用是什么? 如何理解 PyTorch 中的 nn.Module 类?…

#C8# UVM中的factory机制 #S8.1.4# 约束的重载

今天,复习一下《UVM实战》一书中的 关于约束的重载 章节学习。 一 问题引导 文件:src/ch8/section8.1/8.1.2/rand_mode/my_transaction.sv4 class my_transaction extends uvm_sequence_item; …17 constraint crc_err_cons{18 crc_err == 1b0;19 }20 const…

空调遥控器低功耗单片机方案

RAMSUN空调遥控器采用先进的32位低功耗单片机作为核心控制器&#xff0c;通过优化软件算法和硬件设计&#xff0c;实现了空调遥控器的低功耗运行。单片机集成了多种功能模块&#xff0c;包括红外发射、按键扫描、电源管理等&#xff0c;有效降低了整体功耗。同时&#xff0c;该…

结构型——代理模式

结构型——代理模式 代理模式指的是通过创建一个代理来控制对原始对象的访问。代理在客户端与实际对象之间充当“中介” 特点 访问控制&#xff1a;代理对象可以控制对实际对象的访问&#xff0c;从而实现对访问权限的控制。延迟加载&#xff1a;代理对象可以在实际对象被调…

【算法】常见排序算法(插入排序、选择排序、交换排序和归并排序)

文章目录 前言一、排序概念及常见排序算法框图1.排序概念2.常见排序算法框图 二、实现比较排序算法1.插入排序1.1 直接插入排序1.2 希尔排序 2.选择排序2.1 直接选择排序2.2 堆排序 3.交换排序3.1 冒泡排序3.2 快速排序3.2.1 hoare版本3.2.2 挖坑法3.2.3 lomuto前后指针 3.3 快…

Go语言分布式锁实战:dlock助力构建高并发稳定系统

在构建分布式系统时&#xff0c;一个常见且棘手的问题便是资源竞争和数据一致性问题。分布式锁作为一种常用的解决方案&#xff0c;在多个进程或节点之间协调访问共享资源时显得尤为重要。今天&#xff0c;我们将介绍一款分布式锁库——dlock&#xff0c;并通过详细的使用示例带…

算法方法快速回顾

&#xff08;待修改&#xff09; 目录 1. 双指针2. 滑动窗口理论基础 3. 二分查找3. 二分查找理论基础 4. KMP5. 回溯算法6. 贪心算法7. 动态规划7.1. 01背包7.2. 完全背包7.3. 多重背包 8. 单调栈9. 并查集10. 图论10.1. 广度优先搜索&#xff08;BFS&#xff09;10.2. 深度优…

深度学习:让机器学会“思考”的魔法

文章目录 引言&#xff1a;从“鹦鹉学舌”到“举一反三”一、深度学习是什么&#xff1f;1. 定义&#xff1a;机器的“大脑”2. 核心思想&#xff1a;从数据中“悟”出规律 二、深度学习的“大脑”结构&#xff1a;神经网络1. 神经元&#xff1a;深度学习的基本单元2. 神经网络…

电动自行车/电动工具锂电池PCM方案--SH367003、SH367004、SH79F329

在消费电子系统中&#xff0c;如手机电池包&#xff0c;笔记本电脑电池包等&#xff0c;带有控制IC、功率MOSFETFE管以及其他电子元件的电路系统称为电池充放电保护板Protection Circuit Module &#xff08;PCM&#xff09;&#xff0c;而对于动力电池的电池管理系统&#xff…

补码详细分析

补码引入 举一个生活化的例子 假设由一个挂钟&#xff0c;它只能顺时钟调时间&#xff0c;那么它调时间就分成了一下两种情况 正好顺时针调就能调好 如&#xff1a;时针从5调到9需要逆时针调才能调好 如&#xff1a;时针从10调到7 在上面的情况中1是不用处理的&#xff0c;2…

计算机网络入门:物理层与数据链路层详解

&#x1f310; &#xff08;专业解析 中学生也能懂&#xff01;&#xff09; &#x1f4d6; 前言 计算机网络就像数字世界的“高速公路系统”&#xff0c;而物理层和数据链路层是这条公路的基石。本文用 专业视角 和 生活化比喻 &#xff0c;带你轻松理解这两层的核心原理&a…

哪些视频格式在webview2中播放可以设置成透明的?

在WebView2中&#xff0c;能够播放并设置成透明背景的视频格式主要取决于其支持的编解码器以及视频是否包含alpha通道&#xff08;透明度信息&#xff09;。以下是支持透明背景的视频格式&#xff1a; 支持透明背景的视频格式 1. WebM&#xff08;使用VP9编解码器&#xff09; …

【基于ROS的A*算法实现路径规划】A* | ROS | 路径规划 | Python

### 记录一下使用Python实现ROS平台A*算法路径规划 ### 代码可自取 &#xff1a;Xz/little_projecthttps://gitee.com/Xz_zh/little_project.git 目录 一、思路分析 二、算法实现 三、路径规划实现 一、思路分析 要求使用A*算法实现路径规划&#xff0c;可以将该任务分为三…

2025-03-23 吴恩达机器学习3——多维特征

文章目录 1 多元引入2 矢量化2.1 示例2.2 非矢量化实现2.3 矢量化实现2.4 应用 3 特征缩放3.1 举例3.2 必要性3.3 方法3.3.1 最大最小值缩放&#xff08;Min-Max Scaling&#xff09;3.3.2 均值归一化&#xff08;Mean Normalization&#xff09;3.3.3 Z 分数归一化&#xff08…

正点原子内存管理学习和修改

由于项目需要用到内存管理进行动态申请和释放&#xff0c;今天又重新学习了一下正点原子的内存管理实验&#xff0c;温习了一下内存管理的实质。首先先上正点原子内存管理的源代码&#xff1a; malloc.c文件&#xff1a; #include "./MALLOC/malloc.h"#if !(__ARMC…

时空观测者:俯身拾贝

目录 中华文明时空贝壳集&#xff08;按时间排序&#xff09;1. 良渚玉琮&#xff08;约公元前3300-2300年&#xff09;2. 三星堆青铜神树&#xff08;公元前1200年&#xff09;3. 殷墟甲骨文&#xff08;约公元前14世纪&#xff09;4. 京杭大运河&#xff08;公元前486年始建&…