差分数组技巧
简单回顾Tips 1
提示:先回顾一下Tips1的前缀和
前缀和解决的问题是原始数组不会被修改的情况下,频繁的查询某个区间的累加和
核心代码如下:
class PreSum {
private int [] preSum;
//初始化数组
public PreSum(int[] nums){
preSum = new int [nums.length+1];
//构造查询数组
for(int i=1 ; i<nums.lengths ;i++){
preSum[i] = preSum[i-1]+nums[i-1];
}
//返回结果
public int Search(int start ,int end){
return preSum[end+1] - preSum[start];
}
}
而接下来介绍的差分数组是解决频繁对原始数组进行的某个区间内的元素进行频繁的增减操作
一、什么是差分数组呢?
一个数组nums,现在对nums[i…j]区间内的元素全部+3.再对nums[m…n]的元素-2,不会真的有人写for循环两次吧,不会吧!哈哈哈哈哈,还真有,就是我了,虽然我很笨,但这个代码我还是写的出来的
无脑遍历代码,时间复杂度O(N):
int[] count(int start,int end,int val,int[]nums){
if(end>nums.length||start<0) return nums;
for(int i=strat ; i<=end ;i++){
nums[i]+=val;
}
return nums;
}
那现在讲一下用差分数组技巧如何优化:
构造一个diff数组,记录nums[i] 与nums [i-1]的差 ,即diff[i] = nums[i] - nums[i-1]
核心代码:
//构造差分数组:
int [] diff = new int[nums.length];
diff[0] = nums[0];
for(int i=1 ;i<nums.length;i++){
diff[i] = nums[i]-nums[i-1];
}
//差分数组反推原数组:
int[] res = new int [diff.length];
res[0] = diff[0];
for(int i=1;i<diff.length;i++){
res[i] = res[i]+diff[i-1];
}
- 所有频繁对数组区间的加减操作,都使用差分数组进行,最后反推回原数组就行
二、差分数组为什么高效?
优点:
对差分数组操作时间复杂度为O(1)
- 对数组nums[i…j] 全部+3 ,即diff[i]+=3 ; diff [j+1]-=3
diff[i]+=3 意味着从i开始diff数组后面所有的元素全部加了3,即原数组从i-1位置开始全部加了3 ; 我们的目的是 [i,j] 区间 ,所以把 diff[j] 后面的元素再减去3就相当于没操作 ,达到了目的 。
当然倒推回原数组累加的步骤是在最后一次对数组进行操作完之后进行的
也就是说用差分数组diff O(1)的时间复杂度解决了原数组O(N)的时间复杂度!!!
核心代码:
static class Difference{
private int [] diff;
//构造差分数组
public Difference(int[]nums){
assert nums.length>0; //断言,如果nums.length>0就不会抛出错误
diff = new int[nums.length];
diff[0] = nums[0];
for(int i=1;i<nums.length;i++){
diff[i] = nums[i] - nums[i-1];
}
}
//对差分数组进行操作
public void increment(int i,int j ,int[]diff,int val){
diff[i]+=val ;
if(j+1<diff.length) //如果j+1==diff的长度就是对整个数组的操作,不用再减了
diff[j+1]-=val;
}
//返回结果
public int[] result(int[] diff){
int[] res = new int[diff.length];
res[0] = diff[0];
for(int i=1;i<diff.length;i++)
res[i] = res[i-1]+diff[i];
return res;
}
}
实战案例:区间加法
题目:
解决这个问题:秒杀~!
static public void ModifyArray(int length , int [][] updates){
int[] nums = new int[length];
// 使用刚刚封装好的差分数组类
Difference df = new Difference(nums);
for(int[] update :updates){
int start = update[0];
int end = update[1];
int val = update[2];
df.increment(start, end, val); //对差分数组进行操作
}
df.result(); //获得结果
}
实战案例二:
- trips 中包含的就是对差分数组的操作 , capacity就是数组中所有的元素都要小于它
static boolean BusPooling(int[][] trips,int capacity){
//最多1000个车站
int[] nums = new int[1001];
Difference df = new Difference(nums);
for(int[] trip:trips){
int passenager = trip[0];
int add = trip[1];
int multiply = trip[2]-1;
//在 add和multiply区间内车上新加有passenager个乘客
df.increment(add, multiply, passenager);
}
int [] res = df.result();
//检查是否超载
for(int i=0 ; i<res.length;i++){
if(res[i]>capacity)
return false;
}
return true;
}
总结
小小的总结一下:
- 差分数组解决的问题,就是对区间数组进行频繁的加或者减,可以极大的极高算法的效率