差分(Difference numsay)用于高效地对数组进行区间更新操作。它通过记录数组相邻元素之间的差值,从而可以在常数时间内对某个区间内的元素进行增减操作。
差分数组的构造过程如下:
1、创建一个与原数组相同大小的差分数组 diff,初始值都为 0。
2、对于原数组 nums 的相邻元素 nums[i] 和 nums[i+1],将其差值存储在差分数组 diff[i] 中,即 diff[i] = nums[i+1] - nums[i]。
通过差分数组,我们可以在常数时间内对某个区间 [left, right] 内的元素进行增减操作,具体步骤如下:
1、假设要对原数组 nums 的区间 [left, right] 内的元素都增加一个值 val,即 nums[left] += val,nums[left+1] += val, …… ,nums[right] += val。
2、对应的差分数组 diff 的操作为 diff[left] += val 和 diff[right+1] -= val。
在差分数组中,未进行操作之前,diff[left]存储的值是nums[left]与nums[left-1]之间的差值,即diff[left] = nums[left] - nums[left-1]。当对nums[left]进行操作后,加上了一个值val,而nums[left-1]没有加上val,因此nums[left]与nums[left-1]之间的差值发生了变化,变为了nums[left]-nums[left-1]+val。
所以可以将这个变化后的差值表示为:
diff[left] += val
讨论完diff[left]的情况后,我们来看范围[left, right]内的元素差值是否有变化。假设left+1<=right,以diff[left+1]为例,未进行操作之前,diff[left+1]的值是nums[left+1]-nums[left]。在操作后,nums[left+1]和nums[left]都加上了val,所以范围[left, right]内diff值不变。
接下来再来看下diff[right+1],diff[right+1]比较特殊,它还要考虑是否会越界的问题,这里假设right+1小于数组长度,操作前,diff[right+1]=nums[right+1]-nums[right],操作后,nums[right]加了val,nums[right+1]没变,diff[right+1]=nums[right+1]-(nums[right]+val),化简后可得公式。
diff[right + 1] -= val
3、最后,根据差分数组 diff 可以还原回原数组 nums,即对差分数组 diff 进行前缀和操作。
下面是一个使用差分数组进行区间增减操作的示例代码:
public class Main {
public static void main(String[] args) {
int[] nums = {1, 2, 3, 4, 5};
// 构造差分数组
int n = nums.length;
int[] diff = new int[n];
diff[0] = nums[0];
for (int i = 1; i < n; i++) {
diff[i] = nums[i] - nums[i - 1];
}
// 对区间 [left, right] 内的元素增加 val
int left = 1;
int right = 3;
int val = 2;
diff[left] += val;
if (right + 1 < n) {
diff[right + 1] -= val;
}
// 根据差分数组构造结果数组
int[] res = new int[n];
res[0] = diff[0];
for (int i = 1; i < n; i++) {
res[i] = res[i - 1] + diff[i];
}
// 输出结果数组
for (int i = 0; i < n; i++) {
System.out.print(res[i] + " ");
}
System.out.println();
}
}
应用实例:
3.字符串迁移
import java.util.Scanner;
public class d3 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int q = scanner.nextInt();
int a[] = new int[n];
int diff[] = new int[n + 1];
char c[] = scanner.next().toCharArray();
for (int i = 0; i < n; i++) a[i] = Integer.valueOf(c[i] - 'a');
while (q-- > 0) {
int l = scanner.nextInt() - 1;
int r = scanner.nextInt() - 1;
int k = scanner.nextInt() % 26;
diff[l] += k;
diff[r + 1] -= k;
}
for (int i = 1; i < diff.length; i++) diff[i] += diff[i - 1];
for (int i = 0; i < n; i++) {
int t = (a[i] + diff[i]) % 26;
if (t < 0) t += 26; // 处理负数情况
System.out.print((char) ('a' + t));
}
}
}