文章目录
一、认识差分数组
差分数组和前缀和数组的思想类似,但具体应用场景不同。差分数组主要针对某个区间元素频繁地加减操作。这就出现了跟前缀和类似的问题——多次调用for循环,为解决这一问题,我们需创造出差分数组来降低时间复杂度。在此我们通过diff数组来记录多次加减操作,完成任务后再通过反推得到目标数组。通过推算,我们可以得到以下通用模板:以Difference为类,建立Difference、increment和result方法。
代码如下(示例):
// 差分数组工具类
class Difference {
private int[] diff;//差分数组
//构造原始数组并建立差分数组
public Difference(int[] nums) {
assert 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 val) {
diff[i] += val;
if (j + 1 < diff.length) {
diff[j + 1] -= val;
}
}
//反推目标数组
public int[] result() {
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;
}
}
二、算法运用
1.区间加法
选用力扣第 370 题「区间加法」来练习,题目如下:
该题是差分数组的简单应用,把上面的模板复用,在Solution中利用increment方法进行区间的更新。
代码如下(示例):
class Solution {
public int[] getModifiedArray(int length, int[][] updates) {
int[] nums = new int[length];// nums 初始化为全 0
Difference df = new Difference(nums);//构造差分数组
for (int[] update : updates) {
int i = update[0];
int j = update[1];
int val = update[2];
df.increment(i, j, val);
}
return df.result();
}
}
// 差分数组工具类
class Difference {
private int[] diff;//差分数组
//构造原始数组并建立差分数组
public Difference(int[] nums) {
assert 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 val) {
diff[i] += val;
if (j + 1 < diff.length) {
diff[j + 1] -= val;
}
}
//反推目标数组
public int[] result() {
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;
}
}
2.航班统计
下面选用力扣的第 1109 题「航班预订统计」练习,详情请读者自动跳转至原题。
该题类似于上题,不同点在于题目限制,此题要求从n=1开始,所以更新区间要减1,其他的模板复用上一题即可
代码如下(示例):
class Solution {
public int[] corpFlightBookings(int[][] bookings, int n) {
int[] nums=new int[n];//nums初始化全为0
Difference df=new Difference(nums);//构造差分数组
for(int[] booking :bookings){
int i=booking[0]-1;//n从1开始,索引数组范围为[i-1,j-1]
int j=booking[1]-1;
int val=booking[2];
df.increment(i,j,val);//调用increment方法
}
return df.result();
}
class Difference{
private int[] diff;//差分数组
public Difference(int[] nums){
assert 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];
}
}
//对[i,j]加减操作
public void increment(int i,int j,int val){
diff[i]+=val;
if(j+1<diff.length){
diff[j+1]-=val;
}
}
//反推目标数组
public int[] result(){
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;
}
}
}
3.拼车
下面选用力扣的第 1094 题「拼车」练习,详情请读者自动跳转至原题。
该题跟上面两题无异,明确题意,其本质也是差分数组,不同点在于此题要考虑下车和超载情况,所以要考虑end-1和创造检查函数每一次进行检查乘车是否超载
代码如下(示例):
class Solution {
public boolean carPooling(int[][] trips, int capacity) {
int[] nums=new int[1001];//题意限制1000个车站
Difference df=new Difference(nums);//差分数组
for(int[] trip : trips){
int val=trip[0];//乘客数量
int i=trip[1];//上车
int j=trip[2]-1;//下车,乘车范围为trip[1]~trip[2]-1
df.increment(i,j,val);
}
int[] res=df.result();
//检查是否超载
for(int i=0;i<res.length;i++){
if(capacity<res[i]){
return false;
}
}
return true;
}
// 差分数组工具类
class Difference {
private int[] diff;//差分数组
//构造原始数组并建立差分数组
public Difference(int[] nums) {
assert 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 val) {
diff[i] += val;
if (j + 1 < diff.length) {
diff[j + 1] -= val;
}
}
//反推目标数组
public int[] result() {
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;
}
}
}
总结
了解题意,确认运用差分数组后直接套用,再在Solution类中根据题意确认区间和更新数据val,同时也要考虑其他检索条件。diff数组侧重于区间更新,而presum数组侧重于区间的和计算,核心思想差不多,抓住这两点根据题意选择即可