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

news/2024/7/7 19:11:04

一维数组前缀和的概念

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

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://lihuaxi.xjx100.cn/news/859401.html

相关文章

路由器刷固件

前言 我希望可以远程访问我的电脑。但,我不希望电脑总是处于运行状态,因为那样比较费电。所以需要一个方案,能将睡眠/关机中的电脑唤醒。 方案一:选用智能插座,远程给电脑上电。电脑设置上电自启。但,这存…

随着攻击者适应绕过零信任,公司在苦苦挣扎

商业情报公司 Gartner 表示,零信任安全方法有望减少威胁并降低成功攻击的破坏性,但公司不应期望实施零信任原则会很容易或阻止大多数攻击。 虽然对零信任架构的兴趣很高,但目前只有大约 1% 的组织拥有满足零信任定义的成熟程序。 该公司还估…

LightningChart JS v4.0.0 and LightningChart NET

LightningChart JS v4.0.0 引入了新的 DataGrid 组件、全面的折线图类型和视觉主题。2023 年 2 月 9 日 - 16:05 新版本特征 下一代色彩主题: 暗金。网络空间。绿松石六角形。光。光自然。自定义 - 创建您自己的下一代颜色主题。新的 DataGrid 组件 DataGrid 组件是…

虹科方案 | 制药环境中冰箱温度记录的最佳实践——全集成温度监测系统

有效监测冰箱温度是药店、医疗中心和制药实验室的一项重要要求。保持准确的冰箱温度记录对所有储存处方药和疫苗的设施来说是必不可少的,但实现这一目标的最佳方法是什么?● 制药机构需要在特定的温度下储存疫苗和处方药,以保证病人的安全并确…

数据仓库,数据湖

1. 数据仓库 早期系统采用数据库来管理和存放数据,但随着大数据技术的兴起,大家想要通过大数据技术来找到数据之间可能存在的关系,所以大家设计了一套新的数据存储管理系统,把所有的数据全部存储到数据仓库,然后统一对…

RabbitMQ-消息应答

一、介绍为了保证消息在发送过程中不丢失,rabbitmq引入消息应答机制,消息应答就是:消费者在接收到消息并且处理该消息之后,告诉rabbitmq它已经处理了,rabbitmq可以把该消息删除了。二、自动应答消息发送之后立即被认为…

浅谈保护数据的加密策略

加密是一种将信息从可读格式转换为混乱字符串的技术。这样做可以防止数据传输中的机密数据泄露。文档、文件、消息和所有其他形式的网络通信都可以加密。加密策略和身份验证服务的结合,还能保障企业机密信息只对授权用户开启访问权限。常见的数据加密包括以下两种&a…

决策树和期望货币价值

1、决策树和期望货币价值(决策树、表)---风险管理决策树分析是风险分析过程中的一项常用技术。某企业在项目风险分析过程中,采用了决策树分析方法,并计算出了EMV(期望货币值)。以下说法中,正确的…