算法-差分数组


题目: 2772. 使数组中的所有元素都等于零

给你一个下标从 0 开始的整数数组 nums 和一个正整数 k 。

你可以对数组执行下述操作 任意次 :

从数组中选出长度为 k 的 任一 子数组,并将子数组中每个元素都 减去 1 。
如果你可以使数组中的所有元素都等于 0 ,返回  true ;否则,返回 false 。

子数组 是数组中的一个非空连续元素序列。

示例 1:

输入:nums = [2,2,3,1,1,0], k = 3

输出:true

解释:可以执行下述操作:

  • 选出子数组 [2,2,3] ,执行操作后,数组变为 nums = [1,1,2,1,1,0]
  • 选出子数组 [2,1,1] ,执行操作后,数组变为 nums = [1,1,1,0,0,0]
  • 选出子数组 [1,1,1] ,执行操作后,数组变为 nums = [0,0,0,0,0,0]

示例 2:

输入:nums = [1,3,1,1], k = 2

输出:false

解释:无法使数组中的所有元素等于 0
 

提示:

1 <= k <= nums.length <= 105

0 <= nums[i] <= 106

分析

想一想,如果 nums[0]>0nums[0]>0,那么必须要执行什么样的操作?

对于 nums[0]>0 的情况,必须把 nums[0] 到nums[k−1] 都减去 nums[0]

然后思考 nums[1] 要怎么处理,依此类推。

子数组同时加上/减去一个数,非常适合用差分数组来维护。

什么是差分数组

比如我们现在有一个数组arr,arr={0,2,5,4,9,7,10,0}

那么差分数组是什么呢?其实差分数组本质上也是一个数组,我们暂且定义差分数组为d,差分数组d的大小和原来arr数组大小一样,而且di=arri-arri-1,且di=0,它的含义是什么?就是原来数组i位置上的元素和i-1位置上的元素作差,得到的值就是di的值。

那么构造了这么个玩意有什么用呢?难道是来浪费宝贵的内存空间的?嗯,确实是来浪费宝贵的内存了,但是却换了时间上的高效。

现在我们有这么一个区间修改操作,即在区间1~4上,所有的数值都加上3.

我们不要傻傻地遍历arr数组的1,4范围,然后再分别给每个值加上3,我们此时更改差分数组d即可。

显而易见,差分数组d在2,4范围内的值都不用改变,只需要改变差分数组位置1和位置5的值即可,即d1=d1+3,而d5=d5-3,其余不变,为什么呢?因为差分数组的定义——di=arri-arri-1

现在,我们如何根据差分数组d来推测arr中某一个位置的值呢?

比如,此时,我们想知道arr1的值,我们不能直接通过arr1得到原来的值,因为在区间修改的操作中我们并没有修改arr的值,因此我们必须从前往后遍历递推,由于d0=arr0-0(我们定义arr0的前一个数为0),那么arr0=d0=0,又由于d1=arr1-arr0=5,那么arr1=5+arr0=5。以此类推,由于d2=arr2-arr1=3,所以arr2=3+arr1=8。

本意解答

设差分数组为 d。那么把 nums[i] 到 nums[i+k−1] 同时减去 1,等价于把 d[i] 减 d[i+k] 加 1。

注意子数组长度必须恰好等于 k,所以当 i+k≤n 时,才能执行上述操作。

遍历数组的同时,用变量 sumD 累加差分值。遍历到 nums[i] 时,nums[i]+sumD 就是 nums[i] 的实际值了。

分类讨论:

  • 如果 nums[i]<0,由于无法让元素值增大,返回 false。
  • 如果 nums[i]=0,无需操作,遍历下一个数。
  • 如果 nums[i]>0:
    • 如果 i+k>n,无法执行操作,所以 nums[i] 无法变成 0,返回 false。
    • 如果 i+k≤n,按照上面说的执行操作,修改差分数组,遍历下一个数。

如果遍历中途没有返回 false,那么最后返回 true

代码

func CheckArray(nums []int, k int) bool {
   n := len(nums)
   d := make([]int, n+1)
   sum_d := 0
   for i := range nums {
      sum_d += d[i]
      nums[i] += sum_d
      if nums[i] == 0 {
         continue
      }
      if nums[i] < 0 || i+k > n {
         return false
      }
      // 将区间[i,i+k]的每个元素都减去nums[i]。等价于d[i]-=nums[i],d[i+k]-=nums[i]
      sum_d -= nums[i] // 等价于d[i]-=nums[i],没有必要真的操作d[i],因为已经用不到了
      d[i+k] += nums[i]
   }
   return true
}

参考

[1]什么是差分数组

[2]2772. 使数组中的所有元素都等于零


文章作者: Alex
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Alex !
  目录