深入理解前缀和与差分算法及其C++实现

前缀和与差分是算法竞赛和编程中非常重要的两种技巧,它们能够高效地处理区间查询和区间更新问题。本文将详细介绍这两种算法的原理、应用场景以及C++实现。

一、前缀和算法

1.1 前缀和的基本概念

前缀和(Prefix Sum)是一种预处理技术,它通过预先计算并存储数组的前缀和,使得后续的区间和查询可以在常数时间内完成。

给定一个数组arr,其前缀和数组prefix定义为:

prefix[i] = arr[0] + arr[1] + ... + arr[i]

特别地,prefix[0] = arr[0]

1.2 前缀和的构建

以下是构建前缀和数组的C++实现:

vector<int> buildPrefixSum(const vector<int>& arr) {int n = arr.size();vector<int> prefix(n);prefix[0] = arr[0];for (int i = 1; i < n; i++) {prefix[i] = prefix[i-1] + arr[i];}return prefix;
}

1.3 前缀和的应用

前缀和最常见的应用是快速计算区间和。给定区间[L, R],其和可以通过前缀和数组在O(1)时间内计算:

sum(L, R) = prefix[R] - (L > 0 ? prefix[L-1] : 0)

 C++实现:

int rangeSum(const vector<int>& prefix, int L, int R) {if (L == 0) return prefix[R];return prefix[R] - prefix[L-1];
}

1.4 前缀和的变种

1.4.1 二维前缀和

对于二维数组,我们可以构建二维前缀和:

vector<vector<int>> build2DPrefixSum(const vector<vector<int>>& matrix) {int m = matrix.size(), n = matrix[0].size();vector<vector<int>> prefix(m, vector<int>(n));prefix[0][0] = matrix[0][0];for (int j = 1; j < n; j++) prefix[0][j] = prefix[0][j-1] + matrix[0][j];for (int i = 1; i < m; i++) prefix[i][0] = prefix[i-1][0] + matrix[i][0];for (int i = 1; i < m; i++) {for (int j = 1; j < n; j++) {prefix[i][j] = prefix[i-1][j] + prefix[i][j-1] - prefix[i-1][j-1] + matrix[i][j];}}return prefix;
}

计算子矩阵和的公式为: 

sum(x1,y1,x2,y2) = prefix[x2][y2] - (x1>0 ? prefix[x1-1][y2] : 0) - (y1>0 ? prefix[x2][y1-1] : 0) + (x1>0 && y1>0 ? prefix[x1-1][y1-1] : 0)

1.4.2 前缀和的其他应用

前缀和还可以用于:

  • 计算区间平均值
  • 解决子数组和为特定值的问题
  • 统计区间内满足某种条件的元素个数

二、差分算法

2.1 差分的基本概念

差分(Difference)是前缀和的逆运算,它主要用于高效处理区间更新操作。给定一个数组arr,其差分数组diff定义为:

diff[i] = arr[i] - arr[i-1] (i > 0)
diff[0] = arr[0]

2.2 差分的构建

以下是构建差分数组的C++实现:

vector<int> buildDifferenceArray(const vector<int>& arr) {int n = arr.size();vector<int> diff(n);diff[0] = arr[0];for (int i = 1; i < n; i++) {diff[i] = arr[i] - arr[i-1];}return diff;
}

2.3 差分的应用

差分最常见的应用是高效地进行区间更新。要对区间[L, R]内的所有元素增加val,只需要:

diff[L] += val
if (R + 1 < n) diff[R+1] -= val

C++实现: 

void rangeAdd(vector<int>& diff, int L, int R, int val) {diff[L] += val;if (R + 1 < diff.size()) {diff[R+1] -= val;}
}

更新后,可以通过计算前缀和来恢复原始数组: 

vector<int> recoverArray(const vector<int>& diff) {vector<int> arr(diff.size());arr[0] = diff[0];for (int i = 1; i < diff.size(); i++) {arr[i] = arr[i-1] + diff[i];}return arr;
}

2.4 差分的变种

2.4.1 二维差分

对于二维数组,我们可以使用二维差分来处理子矩阵的更新:

void rangeAdd2D(vector<vector<int>>& diff, int x1, int y1, int x2, int y2, int val) {diff[x1][y1] += val;if (x2 + 1 < diff.size()) diff[x2+1][y1] -= val;if (y2 + 1 < diff[0].size()) diff[x1][y2+1] -= val;if (x2 + 1 < diff.size() && y2 + 1 < diff[0].size()) diff[x2+1][y2+1] += val;
}

恢复二维数组: 

vector<vector<int>> recover2DArray(vector<vector<int>>& diff) {int m = diff.size(), n = diff[0].size();vector<vector<int>> arr(m, vector<int>(n));arr[0][0] = diff[0][0];for (int j = 1; j < n; j++) arr[0][j] = arr[0][j-1] + diff[0][j];for (int i = 1; i < m; i++) arr[i][0] = arr[i-1][0] + diff[i][0];for (int i = 1; i < m; i++) {for (int j = 1; j < n; j++) {arr[i][j] = arr[i-1][j] + arr[i][j-1] - arr[i-1][j-1] + diff[i][j];}}return arr;
}

三、前缀和与差分的综合应用

3.1 区间更新+单点查询

使用差分数组可以高效实现区间更新,然后通过计算前缀和实现单点查询。

3.2 区间更新+区间查询

结合线段树或树状数组,可以实现更高效的区间更新和区间查询。

3.3 实际应用示例

问题描述‌:给定一个长度为n的数组,进行m次操作,每次操作将区间[L, R]内的元素增加val,最后输出最终的数组。

解决方案‌:

vector<int> rangeUpdates(int n, vector<vector<int>>& operations) {vector<int> diff(n, 0);for (auto& op : operations) {int L = op[0], R = op[1], val = op[2];diff[L] += val;if (R + 1 < n) diff[R+1] -= val;}vector<int> res(n);res[0] = diff[0];for (int i = 1; i < n; i++) {res[i] = res[i-1] + diff[i];}return res;
}

四、性能分析与比较

4.1 时间复杂度

  • 前缀和:
    • 构建:O(n)
    • 查询:O(1)
  • 差分:
    • 构建:O(n)
    • 更新:O(1)
    • 恢复数组:O(n)

4.2 空间复杂度

两者都需要额外的O(n)空间存储前缀和或差分数组。

4.3 适用场景

  • 前缀和:适用于查询多、更新少的场景
  • 差分:适用于更新多、查询少的场景

五、总结

前缀和和差分是两种互补的算法技巧,它们在处理区间问题时表现出色。前缀和擅长快速计算区间和,而差分擅长高效进行区间更新。掌握这两种技巧可以大大提高解决相关算法问题的效率。

在实际应用中,我们常常需要根据问题的特点选择合适的算法,有时甚至需要将两者结合使用。理解它们的原理和实现方式,对于提升算法能力至关重要。

六、练习题

  1. 给定一个数组,求所有子数组的和的总和。
  2. 给定一个矩阵,求所有子矩阵的和的总和。
  3. 实现一个支持区间加法和区间求和的数据结构。
  4. 解决"最大子数组和"问题。
  5. 解决"和为K的子数组"问题。

希望这篇博客能帮助你深入理解前缀和与差分算法,并在实际编程中灵活运用它们。

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

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

相关文章

HugeGraph【部署】Linux单机部署

注: hugegraph从版本 1.5.0 开始&#xff0c;需要 Java11 运行时环境 一、安装JDK11 1.下载JDK11 https://www.oracle.com/java/technologies/downloads/#java11 2.解压缩包 tar -zxvf jdk-11.0.27_linux-x64_bin.tar.gz 3.修改/etc/profile环境变量 export JAVA_HOME/usr…

C++异步编程里避免超时机制

C标准库中时钟&#xff08;Clock&#xff09; 这段内容主要介绍了C标准库中**时钟&#xff08;Clock&#xff09;**的概念和分类&#xff0c;以及它们在时间测量中的作用。以下是关键信息的解读&#xff1a; 一、时钟的核心特性 C中的时钟是一个类&#xff0c;提供以下四个基…

npm install安装不成功(node:32388)怎么解决?

如果在执行 npm install 时出现问题&#xff0c;尤其是 node:32388 相关的错误&#xff0c;这通常意味着某些依赖或配置出了问题。这里有一些常见的解决方法&#xff0c;你可以尝试&#xff1a; 1. 清除 npm 缓存 有时候&#xff0c;npm 缓存问题会导致安装失败。你可以清除 …

Ubuntu-18.04-bionic 的apt的/etc/apt/sources.list 更换国内镜像软件源 笔记250702

Ubuntu-18.04-bionic 的apt的/etc/apt/sources.list更换国内镜像软件源 笔记250702 为 Ubuntu 18.04 LTS&#xff08;代号 Bionic Beaver&#xff09;更换 /etc/apt/sources.list 为国内镜像源 备份/etc/apt/sources.list文件 sudo cp -a /etc/apt/sources.list /etc/apt/sou…

【运维系列】【ubuntu22.04】安装GitLab

一.下载安装文件 rootgitlab:~# wget https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/9/gitlab-ce-17.4.0-ce.0.el9.x86_64.rpm二.执行安装脚本 2.1 先执行安装前的命令 rootgitlab:~# apt install -y perl-interpreter rootgitlab:~# apt install -y openssh-s…

Cisco ASA防火墙查看ACL的条目数量

这里显示的条目数量为ACE, ACE是啥&#xff1f; ACE全称&#xff1a; access-list entry ACE指的是ACL条目展开后的数量&#xff0c; 啥叫展开&#xff1f; 示例&#xff1a; access-list out-in extend permit tcp80&443 host 1.1.1.1 host 2.2.2.2这种配置是占1条&#…

npm install安装的node_modules是什么

node_modules 是一个由 npm&#xff08;Node Package Manager&#xff09;管理的文件夹&#xff0c;存放着你的 Node.js 项目中所有安装的依赖包。当你运行 npm install 时&#xff0c;npm 会根据你的项目中 package.json 文件中的依赖配置&#xff0c;下载并安装相应的包到 no…

【实时Linux实战系列】实时Linux项目的部署与维护

在实时 Linux 项目的开发过程中&#xff0c;开发阶段的工作仅仅是开始&#xff0c;生产环境中的部署与维护同样至关重要。实时 Linux 系统广泛应用于工业自动化、航空航天、智能交通等对实时性和稳定性要求极高的领域。例如&#xff0c;在工业自动化中&#xff0c;实时系统的部…

Go并发模式精要:掌握Goroutine与Channel的实战艺术

在现代软件开发中&#xff0c;有效利用并发能力已成为提升系统性能的关键。Go语言凭借其原生的Goroutine和Channel机制&#xff0c;为开发者提供了优雅的并发解决方案。本文将深入解析Go并发编程的核心模式与最佳实践。 一、并发基石&#xff1a;Goroutine与Channel // 轻量级…

第29篇:Linux审计系统深度解析:基于OpenEuler 24.03的实践指南

Linux审计系统深度解析&#xff1a;基于OpenEuler 24.03的实践指南 文章目录 Linux审计系统深度解析&#xff1a;基于OpenEuler 24.03的实践指南一、Linux审计系统核心概念与组件架构1.1 审计系统核心组件详解1. auditd守护进程&#xff1a;日志持久化引擎2. auditctl命令行工具…

Linux 启动过程流程图--ARM版

以下是ARM版本Linux启动过程的超详细树状图&#xff0c;涵盖硬件上电到应用程序交互的全流程&#xff0c;并包含关键函数调用链及源码位置&#xff0c;适用于系统开发与调试场景&#xff1a; ARM Linux启动全流程&#xff08;含函数调用链&#xff09; ARM Linux启动流程&…

NVMe高速传输之摆脱XDMA设计6之系统架构设计

结合目前应用需求&#xff0c;以及前面基础分析&#xff0c;确定IP应具有如下特色&#xff1a; &#xff08;1&#xff09; 通用性 前端数据采集系统基于 FPGA 开发。 一方面&#xff0c; 设备类型多&#xff0c; 使用的 FPGA型号各不相同&#xff0c; 需要实现的设计能够在多种…

Mac homebrew 安装教程

下载github安装包 https://github.com/Homebrew/brew/releases/tag/4.5.8 下载安装后 打开 安全里面允许安装&#xff0c;就可以直接使用了

stm32hal模块驱动(1)hpdl1414驱动

之前一直想用hpdl1414画一块手表&#xff0c;前面pcb测试板画完没空调试&#xff0c;最近刚好空出来时间&#xff0c;遂发下驱动。 这里简单赘述hpdl1414的驱动原理&#xff1a;D0-D6负责数据输入&#xff08;ascii表后7位&#xff09;&#xff0c;A0,A1负责更改hpdl1414模块显…

从代码学习深度强化学习 - TRPO PyTorch版

文章目录 前言核心工具函数广义优势估计 (Generalized Advantage Estimation, GAE)案例一:TRPO 解决离散动作问题 (CartPole-v1)1. 环境初始化2. 网络结构定义3. TRPO 智能体实现4. 训练与可视化5. 训练主程序与结果案例二:TRPO 解决连续动作问题 (Pendulum-v1)1. 环境与工具…

MySQL 升级到8.4版本的详细指南

本指南详细介绍了将 MySQL 升级到 8.4 版本的完整流程、注意事项和操作方法。 一、升级前准备 (3.1 Before You Begin) 在开始升级之前&#xff0c;必须仔细审阅本节信息并执行所有推荐的操作&#xff1a; 理解升级过程&#xff1a;了解升级期间可能发生的情况。请参阅第 3.4…

leetcode427.建立四叉树

区间x0到x1和区间y0到y1都是左闭右开的 解题基本思路是先判断当前矩阵是不是全0或全1&#xff0c;如果是就直接返回新建的一个节点值(矩阵的统一值&#xff0c;叶子节点&#xff09;,如果不是那就新建一个节点值&#xff0c;非叶并且左上右上左下右下四个方向上递归创建节点 /…

医学+AI教育实践!南医大探索数据挖掘人才培养,清华指导发布AI教育白皮书

教育数字化浪潮正以前所未有的力度重塑高等教育格局。今年4月&#xff0c;为贯彻落实《教育强国建设规划纲要&#xff08;2024—2035 年&#xff09;》&#xff0c;教育部等九部门印发《关于加快推进教育数字化的意见》&#xff0c;表明将持续推动“人工智能教育”全方位发展&a…

PDF处理控件Spire.PDF系列教程:如何使用C# 拆分 PDF 文件(完整指南)

PDF文件因其高度的跨平台兼容性和安全稳定的格式特点&#xff0c;广泛应用于企业文档管理和电子资料传输中。随着PDF文档页数和内容复杂度的增加&#xff0c;拆分PDF成为优化文档处理流程、提升办公效率的重要需求。通过编程方式实现PDF拆分&#xff0c;不仅能自动化处理海量文…

文心4.5开源模型部署实践

文心4.5开源模型部署实践 使用fastdeploy本地部署 执行命令&#xff1a; python -m fastdeploy.entrypoints.openai.api_server \--model baidu/ERNIE-4.5-21B-A3B-Paddle \--port 8180 \--metrics-port 8181 \--engine-worker-queue-port 8182 \--max-model-len 32768 \--m…