一文详解前缀和:从一维到二维的高效算法应用

文章目录

  • 一、一维前缀和​
    • 1. 基本概念​
    • 2. C++ 代码实现​
    • 3. 应用场景​
  • 二、二维前缀和
    • 1. 基本概念​
    • 2. C++ 代码实现​
    • 3. 应用场景​
  • 三、总结​

在算法竞赛和日常的数据处理工作中,前缀和是一种极其重要的预处理技术。它能够在常数时间内回答多次区间查询,大大提高了算法效率。对于刚接触算法的新手来说,前缀和可能有些抽象,但只要掌握其核心思想和推导过程,就能轻松驾驭。接下来,本文将详细介绍一维前缀和和二维前缀和的原理、推导、实现以及应用场景。​

一、一维前缀和​

1. 基本概念​

一维前缀和的核心思想,是通过记录数组前 i 个元素的累加和,来快速计算数组中任意区间的和。我们以一个简单的数组 arr = [1, 3, 5, 7, 9] 为例,来详细推导前缀和数组的构建过程。​
前缀和数组 s 的定义为:​

  • s[0] = arr[0],在我们的例子中,s[0] = 1,即前缀和数组的第一个元素等于原数组的第一个元素。​
  • i > 0时,s[i] = s[i - 1] + arr[i]。比如计算s[1],根据公式,s[1] = s[0] + arr[1],已知s[0] = 1arr[1] = 3,所以s[1] = 1 + 3 = 4 ;继续计算s[2],s[2] = s[1] + arr[2] = 4 + 5 = 9;以此类推,完整的前缀和数组 s[1, 4, 9, 16, 25]。​

通过前缀和数组,我们就可以快速计算原数组中任意区间[i, j]的和。当i > 0时,区间和为s[j] - s[i - 1],这是因为s[j]包含了从arr[0]arr[j]的所有元素和,而s[i - 1]包含了从arr[0]arr[i - 1]的所有元素和,两者相减,就得到了arr[i]arr[j]的元素和;当i == 0时,区间和直接是s[j],因为此时前缀和数组s[j]就是从原数组开头到arr[j]的所有元素和。​

2. C++ 代码实现​

下面是一维前缀和数组的 C++ 实现代码:

#include <iostream>
#include <vector>
using namespace std;// 计算一维前缀和数组
vector<int> calculatePrefixSum(const vector<int>& arr) {int n = arr.size();if (n == 0) return {};vector<int> prefixSum(n);prefixSum[0] = arr[0];for (int i = 1; i < n; ++i) {prefixSum[i] = prefixSum[i - 1] + arr[i];}return prefixSum;
}// 查询区间[i, j]的和
int queryRangeSum(const vector<int>& prefixSum, int i, int j) {if (i == 0) {return prefixSum[j];} else {return prefixSum[j] - prefixSum[i - 1];}
}int main() {// 示例数组vector<int> arr = {1, 3, 5, 7, 9};// 计算前缀和数组vector<int> prefixSum = calculatePrefixSum(arr);// 查询区间[1, 3]的和(对应原数组的第2到第4个元素)int sum = queryRangeSum(prefixSum, 1, 3);cout << "区间[1, 3]的和为: " << sum << endl; // 输出结果应为3 + 5 + 7 = 15return 0;
}

calculatePrefixSum 函数中,首先处理原数组为空的情况,然后初始化前缀和数组的第一个元素为原数组第一个元素,接着通过循环,根据前缀和公式依次计算出前缀和数组的其他元素。queryRangeSum函数则根据区间起始位置 i 是否为 0,选择不同的计算方式来返回区间和。

3. 应用场景​

一维前缀和的常见应用场景包括:​

  • 频繁查询数组任意区间的和。例如在一个记录每天销售额的数组中,需要频繁查询某段时间内的总销售额,使用一维前缀和就能快速得出结果。​
  • 快速计算数组子数组的和,用于解决子数组和相关问题。比如在一些算法题目中,要求找出和满足特定条件的子数组,前缀和可以帮助我们高效地计算子数组的和,从而快速筛选出符合条件的子数组。​
  • 在统计和数据处理中,快速计算累积数据。如统计学生成绩的累计分数,通过前缀和可以快速得到每个学生及其之前学生的总成绩。

二、二维前缀和

1. 基本概念​

二维前缀和数组是一维前缀和在二维空间上的扩展,常用于快速计算二维数组中任意子矩阵的和。为了便于理解,我们以一个 3×3 的二维数组 matrix 为例进行推导,假设matrix为:

[[1, 2, 3],[4, 5, 6],[7, 8, 9]
]

二维前缀和数组 s 的定义为:s[i][j] 表示从原矩阵左上角 (0, 0) 到右下角 (i, j) 所构成的子矩阵中所有元素的和。​

其递推公式的推导过程如下:​

i > 0j > 0时,s[i][j] = matrix[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1]。我们来分析这个公式,matrix[i][j] 是当前位置的元素;s[i - 1][j] 表示的是从左上角 (0, 0)(i - 1, j) 的子矩阵和,它包含了当前位置左边所有元素的和;s[i][j - 1] 表示从左上角 (0, 0)(i, j - 1) 的子矩阵和,它包含了当前位置上边所有元素的和;而 s[i - 1][j - 1]s[i - 1][j]s[i][j - 1] 中都被计算了一次,即左上角 (0, 0)(i - 1, j - 1) 的子矩阵和被重复计算了,所以要减去一次 s[i - 1][j - 1],这样就能得到从左上角 (0, 0) 到右下角 (i, j) 的子矩阵和。​
对于边界情况,当 i == 0j > 0 时,s[0][j] = s[0][j - 1] + matrix[0][j],因为第一行没有上面的子矩阵,所以前缀和就是前一个位置的前缀和加上当前位置的元素;当 j == 0i > 0 时,s[i][0] = s[i - 1][0] + matrix[i][0],同理,第一列没有左边的子矩阵,前缀和是前一个位置的前缀和加上当前位置的元素;当 i == 0j == 0 时,s[0][0] = matrix[0][0]

通过二维前缀和数组,我们可以在 O (1) 时间内计算出原矩阵中任意子矩阵 [(x1, y1), (x2, y2)] 的和。计算公式为:当 x1 > 0y1 > 0 时,sum = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]。这个公式的原理和递推公式类似,s[x2][y2] 是包含目标子矩阵的大矩阵和,s[x1 - 1][y2]s[x2][y1 - 1] 分别减去了目标子矩阵左边和上边多余的部分,但这样会导致左上角的子矩阵被多减了一次,所以要加上s[x1 - 1][y1 - 1] ;当x1 == 0y1 == 0 时,按照类似的边界情况处理方式进行计算。

2. C++ 代码实现​

下面是二维前缀和数组的 C++ 实现代码:

#include <iostream>
#include <vector>
using namespace std;// 计算二维前缀和数组
vector<vector<int>> calculatePrefixSum2D(const vector<vector<int>>& matrix) {int rows = matrix.size();if (rows == 0) return {};int cols = matrix[0].size();vector<vector<int>> prefixSum(rows, vector<int>(cols, 0));// 初始化前缀和数组的第一行和第一列prefixSum[0][0] = matrix[0][0];// 初始化第一行for (int j = 1; j < cols; ++j) {prefixSum[0][j] = prefixSum[0][j - 1] + matrix[0][j];}// 初始化第一列for (int i = 1; i < rows; ++i) {prefixSum[i][0] = prefixSum[i - 1][0] + matrix[i][0];}// 填充剩余的前缀和数组for (int i = 1; i < rows; ++i) {for (int j = 1; j < cols; ++j) {prefixSum[i][j] = matrix[i][j] + prefixSum[i - 1][j] + prefixSum[i][j - 1] - prefixSum[i - 1][j - 1];}}return prefixSum;
}// 查询子矩阵[(x1, y1), (x2, y2)]的和(闭区间,包含四个端点)
int querySubmatrixSum(const vector<vector<int>>& prefixSum, int x1, int y1, int x2, int y2) {if (x1 == 0 && y1 == 0) {return prefixSum[x2][y2];} else if (x1 == 0) {return prefixSum[x2][y2] - prefixSum[x2][y1 - 1];} else if (y1 == 0) {return prefixSum[x2][y2] - prefixSum[x1 - 1][y2];} else {return prefixSum[x2][y2] - prefixSum[x1 - 1][y2] - prefixSum[x2][y1 - 1] + prefixSum[x1 - 1][y1 - 1];}
}int main() {// 示例二维数组vector<vector<int>> matrix = {{1, 2, 3},{4, 5, 6},{7, 8, 9}};// 计算二维前缀和数组vector<vector<int>> prefixSum = calculatePrefixSum2D(matrix);// 查询子矩阵[(1, 1), (2, 2)]的和(对应原矩阵的右下角2x2子矩阵)int sum = querySubmatrixSum(prefixSum, 1, 1, 2, 2);cout << "子矩阵[(1, 1), (2, 2)]的和为: " << sum << endl; // 输出结果应为5 + 6 + 8 + 9 = 28return 0;
}

calculatePrefixSum2D 函数中,先处理二维数组为空的情况,然后初始化二维前缀和数组,接着分别初始化第一行和第一列,最后通过两层循环,根据二维前缀和的递推公式填充整个前缀和数组。querySubmatrixSum 函数则根据子矩阵的起始位置是否在边界,选择不同的计算方式返回子矩阵的和。​

3. 应用场景​

二维前缀和的常见应用场景包括:​

  • 频繁查询二维数组中任意子矩阵的和。比如在图像像素处理中,可能需要频繁计算图像中某个区域的像素总和,使用二维前缀和就能快速完成计算。​
  • 图像处理中的区域像素和计算。除了求和,基于前缀和还可以计算区域的平均像素值等统计信息,为图像分析提供基础数据。​
  • 地理信息系统中的区域统计分析。在地理信息系统中,将地图数据以二维数组形式存储,使用二维前缀和可以快速计算某个区域内的各种统计数据,如人口总数、土地面积总和等。​

三、总结​

前缀和是一种非常实用的预处理技术,通过构建前缀和数组,可以将区间和查询的时间复杂度从 O (n)O (n²) 降低到 O (1),大大提高了算法效率。无论是一维前缀和还是二维前缀和,理解其推导过程是掌握这项技术的关键。对于新手来说,建议多通过实际例子和练习题来加深对前缀和的理解和应用。希望本文对你理解前缀和算法有所帮助!如果你有任何疑问或建议,欢迎在评论区留言讨论。

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

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

相关文章

windows 开发

文章目录 环境搭建数据库关键修改说明&#xff1a;在代码中使用该连接字符串&#xff1a;注意事项&#xff1a;实际使用 都说几天创造一个奇迹&#xff0c;现在是真的这样了&#xff0c;Just do it! 环境搭建 数据库 需要下载这个SQL Server数据库&#xff0c;然后每次Visua…

免费OCPP协议测试工具

免费OCPP 1.6J协议测试工具&#xff0c;简单实用。除加密功能外&#xff08;后续版本支持&#xff09;&#xff0c;支持所有消息调试。 后续将添加2.01和和2.1协议支持. 欢迎使用 Charge-Test

高等数学基础(行列式和矩阵的秩)

行列式主要用于判断矩阵是否可逆及计算特征方程 初见行列式 行列式起源于线性方程组求解 { a 11 x 1 a 12 x 2 b 1 a 21 x 1 a 22 x 2 b 2 \begin{cases} a_{11}x_1 a_{12}x_2 b_1 \\ a_{21}x_1 a_{22}x_2 b_2 \end{cases} {a11​x1​a12​x2​b1​a21​x1​a22​x2…

开心灿烂go开发面试题

1.给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例1: 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] package main import “fmt” type ListNode struct { Val int Next *ListNode } func main() { l1 : &…

【Flutter】程序报错导致的灰屏总结

【Flutter】程序报错导致的灰屏总结 一、前言 在 Flutter 中&#xff0c;出现“灰屏”&#xff08;grey screen&#xff09;通常意味着 应用发生了未捕获的错误&#xff0c;导致框架无法正确构建 UI。 这也是在面试过程中常常问到的。 二、错误分类 常见的会导致灰屏的错误…

基于物联网设计的智慧家庭健康医疗系统

1. 项目开发背景 随着物联网&#xff08;IoT&#xff09;技术的发展&#xff0c;智能家居系统逐渐融入到我们的日常生活中&#xff0c;成为提高生活质量、增强家庭安全、提升健康管理的重要工具。特别是在健康医疗领域&#xff0c;借助物联网技术&#xff0c;智能家居不仅能够…

设计模式精讲 Day 1:单例模式(Singleton Pattern)

【设计模式精讲 Day 1】单例模式&#xff08;Singleton Pattern&#xff09; 文章内容 开篇 在软件开发中&#xff0c;设计模式是解决常见问题的通用解决方案。作为“设计模式精讲”系列的第一天&#xff0c;我们将深入讲解单例模式&#xff08;Singleton Pattern&#xff09…

【卫星通信】3GPP标准提案:面向NB-IoT(GEO)场景的IMS信令优化方案-降低卫星通信场景下的语音呼叫建立时延

一、引言 随着5G非地面网络&#xff08;NTN&#xff09;技术的演进&#xff0c;基于NB-IoT的卫星通信&#xff08;如GEO地球同步轨道卫星&#xff09;逐渐成为偏远地区语音服务的重要补充。然而&#xff0c;传统IP多媒体子系统&#xff08;IMS&#xff09;的信令流程在带宽受限…

软件测试之简单基础的安全测试方法(另外包含软测面试题库)

文章目录 前言安全测试是什么简单基础的安全测试方法密码安全操作权限验证SQL注入xss脚本攻击文件上传下载安全漏洞扫描Web扫描APP扫描 面试题库&#xff08;仅参考&#xff09;参考目录 前言 阅读本文前请注意最后编辑时间&#xff0c;文章内容可能与目前最新的技术发展情况相…

LCEL:LangChain 表达式语言详解与测试工程师的实践指南

引言 在 AI 应用开发中&#xff0c;如何高效地组合多个步骤&#xff08;如提示模板、模型调用、输出解析&#xff09;并优化执行流程&#xff0c;是开发者和测试工程师共同面临的挑战。LangChain Expression Language (LCEL) 作为 LangChain 的核心功能之一&#xff0c;提供了…

LeetCode面试经典150题—旋转数组—LeetCode189

原题请见&#xff1a;Leetcode189-旋转数组 1、题目描述 2、题目分析 首先容易想到的最简单的方案&#xff0c;是算出来移动K步之后&#xff0c;新数组的每一个坐标与原坐标的映射关系&#xff0c;然后根据映射关系放到一个全新的数组&#xff0c;再把新数组的值赋给原数组。…

2.5 Rviz使用教程

新建终端&#xff0c;键入命令 roslaunch wpr_simulation wpb_simple.launch 再新建终端&#xff0c;键入命令 rviz修改Fix Frame 为 base_footprint 点击add之后选择RobotModel 再增加一个LaserScan 选择激光雷达话题 可视化效果 配置的两种方法 1.在Gazebo运行的基础上&…

基于SpringBoot+JSP开发的招投标采购信息平台

角色&#xff1a; 管理员、普通用户 技术&#xff1a; 后端&#xff1a;Spring Boot Mybatis-Plus MySQL 前端&#xff1a;JSP 核心功能&#xff1a; 该平台是一个用于管理投标和招标信息的系统&#xff0c;主要提供信息发布、用户管理和交易管理等核心功能。 功能介绍…

【项目实训#10】HarmonyOS API文档RAG检索系统后端实现

【项目实训#10】HarmonyOS API文档RAG检索系统后端实现 文章目录 【项目实训#10】HarmonyOS API文档RAG检索系统后端实现一、背景简介二、RAG技术原理与架构设计2.1 RAG技术原理回顾与提升2.2 系统架构设计 三、RAG引擎核心实现3.1 RAG引擎初始化3.2 查询向量化3.3 文档检索实现…

专注于PLC数据采集MES交互解决方案

专注于PLC数据采集MES交互解决方案 前篇文章我们讲到当下的制造行业在工业4.0的大趋势下&#xff0c;MES系统成为现场制造过程管制的有利武器&#xff0c;更是质量追踪的一把好工具。我们要知道产品在各个加工环节的结果。除了人工在各个制造环节录入制造结果外&#xff0c;更…

微信小程序实现文字逐行动画效果渲染显示

1. 微信小程序实现文字逐行动画效果渲染显示 在微信小程序开发中,为了文字逐行动画效果渲染可以通过JavaScript 和 WXML 的动态数据绑定来实现,实现文字逐行显示的效果,同时结合 CSS 动画提升视觉体验。   如果需要更复杂的动画效果(如缩放、移动等),可以使用微信小程序…

Redux 原理深度剖析

1. Redux 实现 定义 Action 和 Reducer 类型&#xff0c;为了简便&#xff0c;先用JavaScript来演示。 1.1. 定义Action和Reducer类型 // 定义 Action 类型 /*** typedef {Object} Action* property {string} type*/// 定义 Reducer 类型 /*** callback Reducer* param {any…

【LangChain】4 基于文档的问答

对于给定的文档, 比如从PDF、网页、公司主页中提取构建的内部文档集合&#xff0c;我们可以使用大语言模型来回答关于这些文档内容的问题&#xff0c;以帮助用户更有效地获取和使用他们所需要的信息。这种方式非常有效且灵活地适用于实际应用场景&#xff0c;因为它不仅仅利用大…

基于Netty的TCP Server端和Client端解决正向隔离网闸数据透传问题

背景 因为安装了正向隔离网闸&#xff0c;导致数据传输的时候仅支持TCP协议和UDP协议&#xff0c;因此需要开发TCP Client和Server服务来将数据透传&#xff0c;当前环境是获取的数据并将数据转发到kafka 1.引入依赖 <dependency><groupId>io.netty</groupId>…

Cursor链接远程服务器实现项目部署

想获取更多高质量的Java技术文章&#xff1f;欢迎访问Java技术小馆官网&#xff0c;持续更新优质内容&#xff0c;助力技术成长 技术小馆官网 在软件开发过程中&#xff0c;远程服务器开发是一种常见的工作模式。通过远程连接服务器进行代码编写和环境配置&#xff0c;可以充分…