差分数组

97 阅读2分钟

当我们需要频繁的对数组nums的某个区间 [i, j] 元素进行加减操作时,按照常规的思路,我们都是直接使用for循环暴力进行加减

//例如对数组元素加上k
func Increment(nums []int, k int) []int {
   for i := range nums {
      nums[i] += k
   }
   return nums
}

通过for循环进行增减次数少的操作时,还可以适用。但是当要频繁的对某个区间进行增减操作时,时间复杂度就会剧增,假设需增减M次,由于每次增减操作平均时间复杂度要去到O(N),最终就会导致整体时间复杂度去到O(M*N)。

差分数组

定义: 查分数组是一个与原数组具有相等长度的数组,其第i个位置的值,表示原数组中第i个位置的值减去原数组第i-1位置的值。即:

//建立差分数组
diff[0] = nums[0]   //i==0
diff[i] = nums[i] - nums[i-1]    //i>0
 
//根据差分数组还原原数组
nums[0] = diff[0]  //当i==0
nums[i] = diff[i] + nums[i-1]  //i>0

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

image.png 根据规则我们建立差分数组: diff[i]= nums[i] - nums[i-1] (i>0)

image.png 现在我们在一个区间[1,4]上进行修改,给区间上的数加上3

image.png 通过差分数组,我们只需要给1位置上的数加上3,中间位置数不动,给4+1位置上的数减3,就完成了所有操作,时间复杂度为O(1)。

解释:当我们还原到原数组时,由于nums[i] = diff[i] + nums[i-1],我们给1号位置加上3后,2、3、4、5位置都会同时加上3,但是我们区间是到4,所以在5号位置上要减3来保持一致

image.png

该方法适用于区间频繁修改,而且这个区间范围是比较大的情况,我们可以将给差分数组绑定方法便于使用

type Diff struct {
   diff []int
}

func (d *Diff) CreateDiff(nums []int) []int {
   diff := make([]int, len(nums))
   diff[0] = nums[0]
   for i := 1; i < len(nums); i++ {
      diff[i] = nums[i] - nums[i-1]
   }
   d.diff = diff
   return d.diff
}
//加减操作
func (d *Diff) Operate(i, j int, value int) {
   d.diff[i] += value
   d.diff[j+1] -= value
}
//返回操作后的原数组
func (d *Diff) Result() []int {
   nums := make([]int, len(d.diff))
   nums[0] = d.diff[0]
   for i := 1; i < len(d.diff); i++ {
      nums[i] = d.diff[i] + nums[i-1]
   }
   return nums
}

文中图片来自:baijiahao.baidu.com/s?id=172681…