算法设计 - 前缀和 差分数列

news2024/11/24 0:40:51

一维数组前缀和的概念

前缀和的概念很简单,我们用一维数组的前缀和来举例,有如下一维数组:

arr = [1, 2, 3, 4, 5]

该数组的前缀和数组如下

preSum = [1, 3, 6, 10, 15]

关系如下:

  • preSum[0] = arr[0]
  • preSum[1] = arr[0] + arr[1]
  • preSum[2] = arr[0] + arr[1] + arr[2]
  • preSum[3] = arr[0] + arr[1] + arr[2] + arr[3]
  • preSum[4] = arr[0] + arr[1] + arr[2] + arr[3] + arr[4]

可以发现前缀和数组元素 preSum[i]的值 其实就是 arr数组 0~i 前缀子数组的和。

而计算得到前缀和数组,可以利用动态规划,其状态转移方程如下:

  • preSum[0] = arr[0]
  • preSum[i] = preSum[i-1] + arr[i] ; (i  >= 1)

一维数组前缀和的应用

一维前缀和可以用于求解数组任意区间的和。

比如有数组 arr = [1,2,3,4,5],我们通过动态规划得到其前缀和数组:preSum = [1,3,6,10,15],现在要求arr给定[l,r]区间范围内元素的和。

一般策略是,for循环遍历arr的l~r索引的元素累加。但是如果给定的区间特别多呢?那就意味着要多次for循环遍历对应区间元素进行累加。这必然产生一个问题:重复计算。

即每次求解给定区间元素和的时间复杂度都是O(n)。

但是利用前缀和,我们就可以在O(1)时间内得出给定区间的元素和。

比如,现在要求arr数组的[l,r]区间的元素和,利用前缀和求解公式如下:

subSum = preSum[r] - preSum[l-1]

比如,arr数组[1,3]区间的和 = preSum[3] - preSum[0] = 10 - 1 = 9

二维矩阵前缀和的概念

二维前缀和即二维矩阵的前缀和,比如现在有如下二维矩阵matrix

preSum[i][j] 表示 左上角(0,0)坐标到右下角(i, j)坐标范围内元素的和,比如:

preSum[2][3]表示的前缀和求解范围如下

那么我们该如何计算preSum[i][j]呢?

此时同样需要利用动态规划,即利用关联状态之间的转移:

preSum[i][j] = preSum[i-1][j] + preSum[i][j-1] - preSum[i-1][j-1] + matrix[i][j]

是不是感觉贼复杂的状态转移公式?

下面我们通过画图,来帮助你理解这个公式:

preSum[i][j]其实就是上图黄色区域

preSum[i-1][j] 如下图区域

preSum[i][j-1]如下图区域 

那么preSum[i-1][j] + preSum[i][j-1]是什么样子呢?其实就是如下区域,并且有重叠部分,就是下图颜色比较重的红色区域:

 

 而这个重叠区域其实就是 preSum[i-1][j-1]。

因此 preSum[i-1][j] + preSum[i][j-1] - preSum[i-1][j-1]其实是如下区域的元素和

 此时,我们发现该区域和preSum[i][j]区域仅仅只相差了一个matrix[i][j]元素(即上图仅剩的黄色元素)。

因此,得到状态转移方程如下:

preSum[i][j] = preSum[i-1][j] + preSum[i][j-1] - preSum[i-1][j-1] + matrix[i][j]

而完整的状态状态方程如下:

  • preSum[0][0] = matrix[0][0]
  • preSum[0][1] = preSum[0][0] + matrix[0][1]
  • preSum[1][0] = preSum[0][0] + matrix[1][0]
  • preSum[i][j] = preSum[i-1][j] + preSum[i][j-1] - preSum[i-1][j-1] + matrix[i][j];(i >=1 && j >=1)

补充的三个红色转移公式,是为了避免第四个公式产生数组越界异常。

二维矩阵前缀和的应用

同一维数组前缀和相同,二维矩阵前缀和也用于在O(1)时间内,求解出二维矩阵中任意矩形范围内元素之和。

比如求解matrix矩阵中,左上角[i1,j1]到右下角[i2,j2]矩形范围的元素之和,如下图

其实这个矩形范围,可以看成是:

(0,0) ~ (i2,j2)  区域  减去 (0,0)~(i2, j1) + (0,0)~(i1, j2) 再加上 (0,0)~(i1,j1)

即计算公式如下:

preSum[i2][j2] - (preSum[i2][j1] + preSum[i1][j2]) + preSum[i1][j1]

这样我们基于二维矩阵前缀和,就能用O(1)的时间计算出任意给定矩形范围内元素之和。

一维数组的差分数列

一维数组的差分数组概念也很简单,比如有一维数组arr = [1, 2, 3, 4, 5],那么arr对应的差分数列即为 diff = [1, 1, 1, 1, 1]

原理是:

  • diff[0] = arr[0]
  • diff[1] = arr[1] - arr[0]
  • diff[2] = arr[2] - arr[1]
  • diff[3] = arr[3] - arr[2]
  • diff[4] = arr[4] - arr[3]

差分数列的求解很简单,即:

  • diff[0] = arr[0]
  • diff[i] = arr[i] - arr[i-1]

那么差分数列有什么用呢?

在介绍差分数列的应用之前,我们需要先了解差分数列的一个特性,那就是差分数列,也可以看出一个数组,那么这个数组的前缀和数组是什么呢?

我们来计算一下:

  • diffPreSum[0] = diff[0]
  • diffPreSum[1] = diff[0] + diff[1]
  • diffPreSum[2] = diff[0] + diff[1] + diff[2]
  • diffPreSum[3] = diff[0] + diff[1] + diff[2] + diff[3]
  • diffPreSum[4] = diff[0] + diff[1] + diff[2] + diff[3] + diff[4]

那么差分数列diff = [1, 1, 1, 1, 1] 对应的前缀和数组diffPreSum = [1, 2, 3, 4, 5]

是不是似曾相识?arr = [1, 2, 3, 4, 5]

没错,arr数组的差分数列是diff,diff的前缀和数组又变回了arr。

即:一个数组arr的差分数列的前缀和数组就是arr

那么为什么diff[4] = 1,diffPreSum[4]就变为了5呢?过程如下:

  • diff[4] = arr[4] - arr[3]
  • diffPreSum[4] = diff[0] + diff[1] + diff[2] + diff[3] + diff[4]
  •                        = arr[0] + (arr[1] - arr[0]) + (arr[2] - arr[1]) + (arr[3] - arr[2] ) + (arr[4] - arr[3])
  •                        = arr[4]

扩展:一个数组arr的前缀和数组的差分数列就是arr

即,差分和前缀和可以看成互逆运算

那么了解了差分数列这个特性有什么用呢?

差分数列通常用于 给一个区间所有元素 统统加上 一个增量。

比如,现在有数组list,现在要给它区间 [l, r] 范围内所有元素加上d。

按照以往思路,我们通常是for循环遍历list数组的 [l, r] 区间每个元素,并给遍历元素+d,这个操作的时间复杂度是O(n)。如果要对多个区间进行+d,那么就需要进行多次for。

但是,我们可以定义一个差分数列 diff,长度和list相同,且数组元素全部初始化为0。

如果要给list数组的 [l, r] 区间每个元素+d,则只需要花费O(1)将:

  • diff[l] += d
  • diff[r+1] -= d

然后求解diff的前缀和数组即可。

比如:list 长度为5,则初始化差分数列 diff = [0, 0, 0, 0, 0]

现在要给list的[1,3]区间每个元素 + 2,则:

  • diff[1] += 2
  • diff[4] -= 2

即 diff 差分数列变为 [0, 2, 0, 0, -2]

然后求解diff差分数列的前缀和diffPreSum为:[0, 2, 2, 2, 0]

最后 遍历list,将list[i] += diffPreSum[i] 即可。

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

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

相关文章

Codeforces Round #851 (Div. 2) A — C

Codeforces Round #851 A. One and Two 题目描述 给定一个序列a中的每个元素都是1或2。找出整数k是否存在,以满足以题目所给条件。 题目分析 1对乘积没有贡献,只需要注意2的个数即可,偶数个2即可满足条件,记录第cnt/2个2的位…

集成nanocaptcha库生成登录验证码

背景 需要实现一个验证码登录的功能需求。这个需求挺简单的,主要实现验证码图片生成给前端,然后,在登录接口比对验证码即可。刚拿到这个需求,好久没有搞过登录这一块了,所以,查了一下相关验证码的知识。下…

cv2--特征点特征提取(Sift,Orb,Surf)

cv2–特征点特征提取(Sift,Orb,Surf) 文章目录cv2--特征点特征提取(Sift,Orb,Surf)1. 关键点和关键点描述子2. Sift2.1 检测的步骤2.2 同时计算关键点kp和描述子des3. Surf4. Orb5. …

61 UseSerialGc的新生代回收调试

前言 呵呵 很久之前看到这样的两篇文章 [讨论] HotSpot VM Serial GC的一个问题 新生代回收调试的一些心得 在第一篇帖子中 R大 详细的讲述了 cheney 算法, 以及自己编写的 cheney 算法, 以及 DefNewGeneration 的具体的一些细节, 以及 和现有的例子的对比 另外还有一些…

leaflet 加载topojson数据,显示图形(代码示例047)

第047个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中加载topojson文件,将图形显示在地图上。TopoJSON文件格式是geoJSON的一种扩展,它可以对地理空间拓扑进行编码。TopoJSON文件包含数据属性和地理空间的属性。 直接复制下面的 vue+openlayers源代码,操作…

C语言入门(什么是C语言,C语言的编程机制以及一些基础计算机概念)

目录 一.什么是C语言 1.面向对象: 2.面向过程: 二.C语言特点 三.C语言开发时间 四.环境搭建 1.代码编辑器 2.C编译器 五.C语言标准 六.计算机补充知识 1.计算机构成 2.CPU工作 3.编译器 七.编写程序步骤 八.源文件,目标文件&a…

SpringBoot 三大开发工具,你都用过么?

本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~ Github地址:https://github.com/…

Sentinel服务熔断功能(sentinel整合ribbon+openFeign+fallback)

目录 1、Sentinel服务熔断功能 一、Ribbon系列 (一)提供者9003/9004 (二)消费者84 二、OpenFeign系列 三、熔断框架比较 2、规则持久化 1、Sentinel服务熔断功能 一、Ribbon系列 (一)提供者9003/9004 …

DAMA数据管理知识体系指南之元数据管理

第11章 元数据管理 11.1简介 按照通常的说法,元数据的定义是“关于数据的数据”,但是其确切含义是什么?元数据与数据的关系就像数据与自然界的关系。数据反映了真实世界的交易、事件、对象和关系,而元数据则反映了数据的交易、事…

技术分享|终端安全防护|ChatGPT会创造出超级恶意软件吗?

ChatGPT是一个强大的人工智能聊天机器人,它使用大量的数据收集和自然语言处理与用户“交谈”,感觉像是和正常的人类对话。它的易用性和相对较高的准确性让用户可以利用它做任何事情,从解决复杂的数学问题,到写论文,创建…

【Linux】操作系统与进程的概念

目录 冯诺依曼体系 注意 为什么CPU不直接访问输入或输出设备? 跨主机间数据的传递 操作系统 管理 进程 描述进程 进程的查看和终止 bash 通过系统调用创建子进程 fork的辨析 冯诺依曼体系 🥖冯诺依曼结构也称普林斯顿结构,是一种将…

(超详细)Navicat的安装和激活,亲测有效

步骤一:准备安装包 下载Navicat,我用的v15最好一致(私信可以发你安装包和注册码)步骤二:关闭杀毒软件,然后需要断掉网络(一定断网) 步骤三:一路next安装,安装…

nodejs如何实现Digest摘要认证?

文章目录1.前言2. 原理3. 过程4. node实现摘要认证5. 前端如何Digest摘要登录认证(下面是海康的设备代码)1.前言 根据项目需求,海康设备ISAPI协议需要摘要认证,那么什么是摘要认证?估计不少搞到几年的前端连摘要认证都…

每日一个解决问题:事务无法回滚是什么原因?

今天在码代码时发现事务不回滚了,学过MySQL 事务小伙伴们都懂,通过 begin 开启事务,通过 commit 提交事务或者通过 rollback 回滚事务。 正常来说,当我们开启一个事务之后,需要 commit 或者 rollback 来结束一个事务的…

下面这段Python代码执行后的输出结果是?

点击上方“Python爬虫与数据挖掘”,进行关注回复“书籍”即可获赠Python从入门到进阶共10本电子书今日鸡汤几行归塞尽,念尔独何之。大家好,我是皮皮。一、前言前几天在Python青铜交流群【桐霄L】问了一个Python基础的问题,这里拿出…

TortoiseGit 使用教程

一、下载工具 这里给大家准备了所有安装包自取 链接:https://pan.baidu.com/s/1xrxxgyNXNQEGD_RjwKnPMg 提取码:qwer 也可在官网自行下载最新版 1.下载git,直接去官网下载Git - Downloads,根据自己的系统合理下载&#xff0c…

openGauss客户端安装

目录1. 准备两台Linux系统2. 安装openGauss客户端3. 设置客户端主机环境变量4. 修改服务端配置文件5. 测试客户端远程连接客户端环境:openEuler release 22.03 (LTS-SP1) 服务端环境: openEuler release 20.03 (LTS-SP3) openEuler系统官网下载 1. 准备两台Linux系…

尚医通(十)数据字典加Redis缓存 | MongoDB

目录一、Redis介绍二、数据字典模块添加Redis缓存1、service_cmn模块,添加redis依赖2、service_cmn模块,添加Redis配置类3、在service_cmn模块,配置文件添加redis配置4、通过注解添加redis缓存5、查询数据字典列表添加Redis缓存6、bug&#x…

[oeasy]python0080_设置RGB颜色_24bit_24位真彩色_颜色设置

RGB颜色 回忆上次内容 上次 首先了解了 索引颜色 \33[38;5;XXXm 设置 前景为索引色\33[48;5;XXXm 设置 背景为索引色 RGB每种颜色 可选0-5总共 6 级 想用 精确RGB值 真实地 大红色画个 大红桃心 ♥️ 有可能吗??🤔 rgb 模式 关于 RGB 模式…

07- 梯度下降优化(Lasso/Ridge/ElasticNet) (机器学习)

归一化: 减少数据不同数量级对预测的影响, 主要是将数据不同属性的数据都降到一个数量级。 最大值最小值归一化:优点是可以把所有数值归一到 0~1 之间,缺点受离群值影响较大。0-均值标准化: 经过处理的数据符合标准正态分布,即均值为0,标准差…