一维差分总结
适用情景
如果要对数组的多个区间内的元素进行增减操作,可以考虑使用差分数组进行批量化操作。
思路
一般思路
差分数组实质上是前缀和数组的逆过程。
如果要对区间[l, r]
内的元素加val
,先求差分数组diff
,diff
数组大小为n+2
, 可以避免讨论边界条件,其中diff
数组内元素为原数组前后两个元素的差。接下来依次按如下过程操作:
- diff[l+1] += val
- diff[r+2] -= val
最后对整个差分数组进行求取前缀和,所得的数组即为修改后的数组。
例如,对数组nums = [2, 3, 4, 5]
的[1, 3]
区间内的所有元素+1
,可以使用如下代码进行操作:
1 | def f(nums: list[int], l: int, r: int) -> None: |
输出示例
1 | 2 4 5 6 |
等差数列的一维差分
这部分的思想是,对于某个区间内的元素,加上某个等差数列,例如,对某个区间[1, 2, 3, 4]
,我们想让该区间的所有元素加上一个等差数列an = 1 + (n - 1) * 1 = n
。换句话说,我们期待得到[1+1, 2+2, 3+3, 4+4]
,即[2, 4, 6, 8]
的结果。那么,我们可以从结果出发,进行推导。
为了简单,假设这样一个数组[0, 0, 0, 0, 0, 0, 0, 0]
,设变量s
为首项,d
为公差,e
为末项。[0, s, s+d, s+2d, s+3d, e, 0, 0]
是我们的期望。
注意到,对[0, s, d, d, d, d, -e, 0]
进行前缀和处理,能得到我们期望的结果。为了方便,我们将这个数组设为arr1
。这时候,我们再想,是否有一个差分数组arr2
,通过类似diff[l+1] += val diff[r+2] -= val
的操作,经过前缀和处理,能得到arr1
吗?
这样的数组,是存在的。我们说,差分数组实质上是前缀和数组的逆过程。那么,对于arr1
的1号元素s
,其实可以通过0 + x = s
得到,这里的x
是未知数,表示arr2
第i
号位置的数,这里的i
为1
。同理,对于arr2
的2号元素d
,可以通过s + x = d
得到,求得x = d - s
。同理,可以得到arr2
其他位置上的元素。最后,求得arr2 = [0, s, d - s, 0, 0, 0, -e-d, e]
。
也就是说,我们只需要求出diff
差分数组,接着,按如下步骤操作:
- diff[l+1] += s
- diff[l+2] += (d - s)
- diff[r+2] -= (e + d)
- diff[r+3] += e
最后对该差分数组进行两次前缀和处理,就可以得到[0, s, s+d, s+2d, s+3d, e, 0, 0]
例题分析
给定一个长度为 n 的整数数组 nums 和一个二维数组 queries,其中 queries[i] = [li, ri]。
对于每个查询 queries[i]:
在 nums 的下标范围 [li, ri] 内选择一个下标
子集。将选中的每个下标对应的元素值减 1。
零数组 是指所有元素都等于 0 的数组。
如果在按顺序处理所有查询后,可以将 nums 转换为 零数组 ,则返回 true,否则返回 false。
解题
这题和1109. 航班预订统计类似,都需要我们在一个数组上的多个区间进行修改操作。因此,我们可以使用差分数组进行分析。
假设我们不考虑排除某一个下标做修改,对于每一个区间,我们对其每个元素都进行修改操作。我们申请一个长度为n+2
,全为0
的差分数组,对所有区间修改操作结束后,如果差分数组中,存在某个值小于nums[i]
,说明修改操作不足以将这个数减为0
。因为我们是可以在某一个操作中不选择修改这个元素的,所以如果这个数能变为0
,那么差分数组应该满足diff[i] >= nums[i]
。
由此,可以写出代码:
1 | class Solution { |
时间复杂度分析
假设数组长度为n
,执行的操作为k
,显然,时间复杂度应该为O(n*k)