文章目录
前言
在学习算法的时候,常常需要写算法题,有时候会遇到使用区间动态增加这样的问题,使用差分就是一个很好的方法。如果有哪里写的不好请大家多多指教!!
差分数组(Difference Array)是一种巧妙的数据结构,常用于处理序列的各种动态修改问题,尤其是具有前后元素关联的操作。它的核心理念在于通过记录每个位置与其前一个位置之间的差异值,简化对原数组的频繁更新和查询操作,极大地提高了算法效率。本文将详细介绍差分数组的概念、原理,并结合C++代码实例演示其实战应用。
一、什么是差分数组?
差分数组原理
差分数组D[i]由原数组A[i]衍生而来,其中D[i] = A[i] - A[i-1](通常约定D[0] = A[0])。这样一来,原数组中任意位置i的值可以通过累加前面所有差分值来恢复:A[i] = D[0] + D[1] + … + D[i]。
优点:
- 减少更新操作的时间复杂度:对原数组的一个区间进行增减操作时,只需更改相应区间的差分值。
- 查询区间和或区间更新变得简便快捷。
看到一个很棒的理解:差分笔记
前缀和可以看这里:前缀和
差分数组与前缀和数组互为数学意义上的逆运算关系,犹如微积分中的求导与积分过程。具体而言,若对差分数组执行前缀和操作,则可还原出原数组;反之,若对前缀和数组进行逐项差分处理,同样也能得到原数组。
差分数组的特性体现在区间修改操作上:当需要对原数组的一个连续子区间 [l, r] 增加一个常数值 inc 时,相应的差分数组 d 的变化规则是在索引 l 处增加 inc,在索引 r+1 处减少 inc(注意,这里假设差分数组的大小与原数组相同并包含额外的边界条件)。这种操作具有可叠加性,即多个修改指令可以连续应用而不影响最终结果。
举例来说,(原数组和差分数组下标都从1开始,不然会越界)假定有数组d=[1,2,3,4,5,6],对d[3]到d[5]之间的所有数加上3,变为d=[1,2,6,7,8,6],那么差分数组也就从[1,1,1,1,1,1]变成了[1,1,4,1,1,-2]。
因此,对于原数组的区间更新需求,无需直接操作原数组,而是可以通过简洁地按照上述规则修改差分数组来间接实现,这对于涉及大量区间修改查询操作的问题尤为高效。
手推上述过程:
二、差分数组的应用场景和代码示例
1.差分数组的应用场景
- 在在线算法竞赛或实际工程中,处理区间加法、区间查询和单点更新等问题时,差分数组往往能发挥巨大作用。
- 实现滑动窗口最大值、区间统计、动态维护序列统计信息等任务。
2.案例与代码
区间修改与查询: 假设有一个数组,我们需要支持两种操作:
- 将某一段区间的元素都增加一个常数值;
- 查询某一段区间的元素和。可以利用差分数组轻松实现。
代码实现:
#include <iostream>
#include <vector>
class DifferenceArray {
private:
std::vector<int> diffArr; // 差分数组
std::vector<int> originalArr; // 原始数组(仅用于还原)
void update(int l, int r, int val) { // 区间修改
diffArr[l] += val;
if (r + 1 < diffArr.size()) {
diffArr[r + 1] -= val;
}
}
int getSum(int l, int r) const { // 区间查询
int sum = 0;
for (int i = l; i <= r; ++i) {
sum += diffArr[i];
}
return sum + originalArr[0]; // 加上初始值
}
void initFromOriginal(const std::vector<int>& arr) {
originalArr = arr;
diffArr.resize(arr.size());
for (size_t i = 1; i < arr.size(); ++i) {
diffArr[i] = arr[i] - arr[i - 1];
}
}
public:
DifferenceArray(const std::vector<int>& arr) {
initFromOriginal(arr);
}
void modifyRange(int l, int r, int val) {
update(l, r, val);
}
int queryRange(int l, int r) const {
return getSum(l, r);
}
};
int main() {
std::vector<int> arr = {1, 2, 3, 4, 5};
DifferenceArray da(arr);
da.modifyRange(1, 3, 10); // 将区间[1, 3]的元素增加10
std::cout << da.queryRange(0, 4) << std::endl; // 查询整个数组的和
return 0;
}
以上代码中,我们首先定义了一个DifferenceArray类,该类内部维护了差分数组diffArr和原始数组originalArr。update方法用于对区间[l, r]进行修改,只需要更新l和r+1处的差分值即可。getSum方法通过累加差分数组得到指定区间内的元素和。
案例推广(实际算法题中得巧用):
空调(AcWing)
总结
使用差分数组的注意事项
初始化差分数组时,需先构建原始数组,并根据原始数组生成差分数组。
更新差分数组时,要注意处理边界情况,尤其是区间结尾可能超出数组长度的情况。
恢复原始数组的值时,需要从差分数组累加得出,且初始值不能忽略。
总结
差分数组作为一种高效的数据结构,以其独特的思想和便捷的操作性,在处理一些与区间相关的动态问题时表现出色。理解和掌握差分数组,有助于我们在算法设计和编程实践中提高解决问题的效率。在实际应用中,适时结合差分数组与其他数据结构或算法,可以进一步拓宽问题解决思路,优化程序性能。