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

article/2025/10/2 0:06:02

一维数组前缀和的概念

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

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://chatgpt.dhexx.cn/article/ohq7F8d1.shtml

相关文章

python 前缀和总结

前缀和是数据结构与算法中比较重要的知识,前缀和经常可以结合哈希表解决很多有意思的问题。为了方便学习,在这里总结leetcode中出现的前缀和问题。 525. 连续数组 给定一个二进制数组 nums (只含有0,1), 找到含有相同…

【c++入门(2)】前缀和

大纲 1.计算前缀和 2.计算字段和 3.后缀和 4.前缀积 5.后缀积 6.例题 1.计算前缀和 基础问题&#xff1a; 思路1&#xff1a; 枚举 cin (); for (int i 1; i < q; i) {cin >> x;int sum 0;for (int j 1; j < x; j){sum a[j];}cout << sum << endl…

前缀和

【前缀和】 什么是前缀和&#xff1f;前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和。 设b[]为前缀和数组&#xff0c;a[]为原数组&#xff0c;根据这句话可以得到前缀和的定义式和递推式&#xff1a; 定义式递推式一维前缀和 二维前缀和 【一维前缀和】…

Java前缀和算法

一.什么是前缀和算法 通俗来讲&#xff0c;前缀和算法就是使用一个新数组来储存原数组中前n-1个元素的和&#xff08;如果新数组的当前元素的下标为n&#xff0c;计算当前元素的值为原数组中从0到n-1下标数组元素的和&#xff09;&#xff0c;可能这样讲起来有点抽象&#xff0…

基础算法——前缀和详解

秋名山码民的主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f64f;作者水平很有限&#xff0c;如果发现错误&#xff0c;一定要及时告知作者 前言 由于有些读者朋友私聊我&#xff0c;希望出几期基础算法的讲解&#xff0c;kmp&…

前缀和算法

文章目录 前言一、关于前缀和二、一维数组求前缀和1.求段区间前缀和2.例题&#xff1a;AcWing795. 前缀和AC代码 三、二维数组求前缀和1.求S[i,j]2.求&#xff08;x1&#xff0c;y1&#xff09;&#xff0c;&#xff08;x2&#xff0c;y2&#xff09;子矩阵的和3.例题&#xff…

前缀和详解

目录 前缀和概念前缀和代码前缀和例题题目介绍思路分析相关代码 总结 前缀和概念 前缀和&#xff1a;顾名思义&#xff0c;是要求前缀的总和&#xff0c;什么是前缀&#xff0c;对于一个存放数字的数组而言&#xff0c;前缀就是指的数组的前k项&#xff0c;因此对应的前缀和就…

前缀和与差分 图文并茂 超详细整理(全网最通俗易懂)

目录 1、前缀和2、前缀和算法有什么好处&#xff1f;3、二维前缀和4、差分5、一维差分6、二维差分 1、前缀和 前缀和是指某序列的前n项和&#xff0c;可以把它理解为数学上的数列的前n项和&#xff0c;而差分可以看成前缀和的逆运算。合理的使用前缀和与差分&#xff0c;可以将…

算法:解数独

题目内容 编写一个程序&#xff0c;通过已填充的空格来解决数独问题。 一个数独的解法遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 本题满足以下设定&#xff1a; …

算法_回溯_解数独

文章目录 解数独1.解法2.总结python算法 解数独 leetcode链接 1.解法 这道题是一个棋盘问题&#xff0c;而棋盘问题是一个典型的回溯问题。 首先思考普通人&#xff08;这里指没有受过专业训练的人&#xff09;解数独的方法&#xff0c;那就是首先在空白位置假设一个数字&a…

Java编程实战12:解数独

目录 解数独题目示例 1提示 解答解题思路完整代码 解数独 题目 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内…

使用java解数独

说明 先根据规则解数独, 规则1: 如果备选数字只有一个, 那么就填入这个数字规则2: 如果在3*3单元格中, 或者一行, 或者一列中, 某个备选数字在所有的备选数字中只出现了一次, 那么就填入这个数字. 再暴力破解数独, 依次填入备选数字, 如果不能解开, 换下一个备选数字, 直到数独…

力扣 37. 解数独

一、题目描述 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。数独部分空格内已填入了数字&#xf…

LeetCode算法 —— 解数独

题目如下所示&#xff1a; 编写一个程序&#xff0c;通过已填充的空格来解决数独问题。 一个数独的解法需遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 空白格用 ‘.…

C语言——解数独程序[源码]

用C语言写的解数独的程序。在linux下测试成功运行。 效果如图&#xff1a; 这是带解的数独&#xff0c;需要填写的部分用数字0代替。 这是程序运行后的效果图。看看&#xff0c;数独已经搞定啦&#xff5e;&#xff5e;&#xff5e; 程序源码如下&#xff1a; #include <…

用 Python 解数独(Sudoku)

芬兰数学家因卡拉花费3个月时间设计出的世界上迄今难度最大的数独。数独是 9 横 9 竖共有 81 个格子&#xff0c;同时又分为 9 个九宫格。规则很简单&#xff1a;每个空格填入 1~9 任意一个数字&#xff0c;需要保证每个横排和竖排以及九宫格内无相同数字。 解数独是一个可有可…

解数独c++

解数独 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图&#xff09; 数…

MATLAB 解数独

数独是一种较为常见的游戏&#xff0c;一般有4乘4、6乘6、9乘9几种&#xff0c;就像下面这种&#xff0c;本文也是主要求解此类数独。 此外还有一些奇形怪状的&#xff0c;如下图两种&#xff0c;均是不规则的&#xff0c;在此并不会涉及&#xff0c;但会在以后发布代码以及过程…

Leetcode 解数独

解数独 题目描述&#xff1a; 编写一个程序&#xff0c;通过填充空格来解决数独问题。一个数独的解法需遵循如下规则&#xff1a;数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。提示&#xff1a;…

再战leetcode (解数独)

37. 解数独 题目描述 回溯法 leetcode题解 代码 public class Test {public static void main(String[] args) {char[][] board {{5, 3, ., ., 7, ., ., ., .},{6, ., ., 1, 9, 5, ., ., .},{., 9, 8, ., ., ., ., 6, .},{8, ., ., ., 6, ., ., ., 3},{4, ., ., 8, ., 3, .…