前缀和、差分数组详解

写在前面:

写这篇文章的初衷是在刷leetcode第1109号题,也就是本文中的例题时,对前缀和、差分数组的概念以及如何运用比较陌生,百度出一批csdn、博客园的文章出来后,发现内容都千篇一律,互相借鉴;简单解释、公式、性质一列,给个例题的实现代码,一篇文章就完成了。现csdn、博客园上这样的文章一大把,若是这种水平的文章也能称为技术博文,不敢苟同。

但在csdn和博客园这类技术分享平台上,还是不乏许多好文章;在求解过程中读到好文章,让人心情愉悦,无比舒畅。本人现技术水平有限,在学习过程中不敢说自己写出什么技术博文,但力求对所答之惑,做到条理清晰、逻辑通畅、通俗易懂。

若在文中出现错误,请评论指出,将十分感谢。写文章实属不易,若觉得本文不错、或有帮助到你,希望能点赞一下,将十分感谢。

点击获取 文章同步更新链接

前缀和: 数组 d[i] 的对应的前缀和为:d[0] + d[1] + ··· + d[i]

假设数组 d[5] = [ 1, 3, 4, 6, 9 ],该数组的前缀和数组 prefixSum[5] = [ 1, 4, 8, 14, 23]

差分数组: 数组 d 对应的差分数组的第 i 个元素 diff[i] = d[i] - d[i-1],(i > 0, diff[0] = d[0])

假设数组 d[5] = [ 1, 3, 4, 6, 9 ] ,该数组对应的差分数组 diff[5] = [ 1, 2, 1, 2, 3 ]

前缀和差分数组一般是结合起来应用的;原数组、前缀和数组、差分数组,三者可以互相转换;一般是给一个初始数组,然后根据题目条件结合数组特点、规律,利用前缀和或差分数组求解题目;

规律、性质:

  1. 对于前缀和数组,原始数组中的第 i 个元素的变化(加、减等数学运算)都会影响到前缀和数组中第 i 个元素之后的所有元素;

一般应用:

  1. 对数组中某个区间上的每个元素加上或减去同一数值;
  2. 对数组中某个区间上的每个元素进行累加、累减、累积等操作;

下面以一个例题进行详细分析:

1109. 航班预订统计

这里有 n 个航班,它们分别从 1 到 n 进行编号。

有一份航班预订表 bookings ,表中第 i 条预订记录 bookings[i] = [firsti, lasti, seatsi] 意味着在从 firsti 到 lasti (包含 firsti 和 lasti )的 每个航班 上预订了 seatsi 个座位。

请你返回一个长度为 n 的数组 answer,其中 answer[i] 是航班 i 上预订的座位总数。

示例 1:

输入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
输出:[10,55,45,25,25]
解释:
航班编号 1 2 3 4 5
预订记录 1 :10 10
预订记录 2 : 20 20
预订记录 3 : 25 25 25 25
总座位数: 10 55 45 25 25
因此,answer = [10,55,45,25,25]

示例 2:

输入:bookings = [[1,2,10],[2,2,15]], n = 2
输出:[10,25]
解释:
航班编号 1 2
预订记录 1 : 10 10
预订记录 2 : 15
总座位数: 10 25
因此,answer = [10,25]

提示:

1 <= n <= 2 * 1 0 4 10^4 104
1 <= bookings.length <= 2 * 1 0 4 10^4 104
bookings[i].length == 3
1 <= firsti <= lasti <= n
1 <= seatsi <= 1 0 4 10^4 104

分析:

最常规的就是暴力求解了,即 answer[i] += bookings[][i],暴力求解时间复杂度为O( n 2 n^2 n2)

本题利用前缀和可以求解的时间复杂度降为O( n n n),下面对题目条件进行分析,看看是如何使用前缀和进行求解的;

  1. 根据题目条件,我们需要根据 bookings[i] = [firsti, lasti, seatsi],对answer如下更新操作:

    answer[firsti] += seatsi,answer[firsti+1] += seatsi,··· , answer[lasti] += seatsi;firsti != lasti。若firsti == lasti,则answer[first] += seatsi;

  2. 由answer的更新操作可知,对answer的更新,是在 [firsti, lasti] 区间上,每个元素都加上seatsi;因为answer在区间 [firsti, lasti] 上每个元素加上的是相同的值,由前缀和的概念和上述第一条性质、规律,可以把对answer在区间 [firsti, lasti] 上的每个元素的更新操作,转换为对answer[firsti] 和answer[lasti]两个元素的更新操作;

  3. 如何理解这种转换操作呢?

    首先我们确定本题的answer中每个元素的初始值为0;我们令下列举例的数组 d 中每个元素的初始值也为0;

    我们知道数组 d[i] 的对应的前缀和为:prefixSum[i] = d[0] + d[1] + ··· + d[i] ;若对数组 d ,在区间 [0, i] 上每个元素都加上相同的一个值 x,我们令 d[0] += x ,则 d[j] (0<j≤i) = prefixSum[j] (d中每个元素初始化为0);这样就把对区间中每个元素都加上相同的一个值的更新操作转换为只对对answer[first]一个值的更新操作,即 answer[firsti] += seatsi ,然后计算answer中每个元素的前缀和prefixSum[i]来计算出最后的结果;即 answer[i] = prefixSum[i]

    那对answer[lasti]的更新操作如何理解呢?

    有了 answer[firsti] += seatsi 这步操作的理解,相信对前缀和的概念有了很好的理解;先直接给出 answer[lasti] 的具体更新步骤,然后在对其进行解释 ;

    answer[lasti + 1] -= seatsi ,(lasti + 1 ≤ n)

    题中 bookings 给出了多个需要进行更新操作的区间 [firsti,lasti] 和更新的数值 seatsi,我们通过把对区间上的每个元素都加上一个相同的数转换为 answer[firsti] += seatsi,然而因为区间会存在重叠的情况,需要去掉重叠的部门的,避免前缀和 prefixSum[i] 的计算出现冗余;不然在根据bookings更新answer后,最后通过计算answer的前缀和数组prefixSum得出的结果是错误的。

  4. 通过问题转换来理解answer[firsti] += seatsianswer[lasti + 1] -= seatsi 这两步操作(来源LeetCode题解 ,作者:LuoRong1994)

    换一种思路理解题意,将问题转换为:某公交车共有 n 站,第 i 条记录 bookings[i] = [i, j, k] 表示在 i 站上车 k 人,乘坐到 j 站,在 j+1 站下车,需要按照车站顺序返回每一站车上的人数。

**注:**数组的下标是从0开始的,实际的更新操作为:answer[firsti - 1] += seatsianswer[lasti] -= seatsi

实现代码:

vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
	vector<int> answer(n);
	for (auto book : bookings)
	{
		answer[book[0] - 1] += book[2];
		if (book[1] < n)
			answer[book[1]] -= book[2];
	}
	for (int i = 1; i < n; i++)
	{
		answer[i] = answer[i] + answer[i - 1];
	}
	return answer;
}

举一反三:

238. 除自身以外数组的乘积 :区间上元素的累积

1685. 有序数组中差绝对值之和:利用前缀和求解

总结:

  1. 根据上述所讲的一般应用,初步判断是否可以使用前缀和、差分数组的知识求解;
  2. 结前缀和、差分数组这类题目,最关键的步骤是把对数组区间上元素的加、减、乘积等操作用数学公式列出来,观察是否符合前缀和、差分数组的数学公式特征,若符合,再根据题目具体条件变换公式,编码求解;
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值