一、核心定义:用差值记录变化
差分数组 diff 是原数组 nums 的 “变化量数组”,定义为:
diff[0] = nums[0](首元素差值为自身)diff[i] = nums[i] - nums[i-1](后续元素为相邻差值)
例:若 nums = [1, 3, 5],则 diff = [1, 2, 2]。
- 原数组可通过
diff的前缀和还原:1, 1+2=3, 1+2+2=5。
二、关键操作:O (1) 区间更新
若需对原数组区间 [l, r] 内所有元素 加 x,只需修改差分数组的两个端点:
diff[l] += x:标记区间起点,后续元素开始累加x。diff[r+1] -= x:标记区间终点的下一位,抵消后续累加(若r+1超出数组范围,可忽略)。
原理:通过前缀和计算时,[l, r] 内的元素会包含 x,而 r 之后的元素不受影响。
例:对 nums = [1,3,5] 的区间 [1,2] 加 2:
diff = [1, 2, 2]→ 修改后diff = [1, 4, 0, -2](假设长度为 4)。- 前缀和还原:
1, 1+4=5, 5+0=5, 5-2=3→ 原数组变为[1,5,5]。
三、实战应用:统计区间覆盖次数
题目场景
给定数组 nums 和查询列表 queries,每个查询 [l, r] 表示:在区间 [l, r] 内选择子集减 1。判断能否通过若干次操作将数组变为全零。
核心思路
- 每个元素
nums[i]最多可减的次数等于 被查询区间覆盖的次数(记为cnt[i])。 - 若
nums[i] ≤ cnt[i],则可通过选择子集减nums[i]次使其为 0。
差分数组的作用
- 记录覆盖次数:
每个查询[l, r]等价于对区间[l, r]的覆盖次数加 1,通过差分数组实现:diff[l]++; // 区间起点加1(开始计数) diff[r+1]--; // 区间终点的下一位减1(停止计数) - 计算前缀和得到覆盖次数:
遍历差分数组,累加得到sum_d,即当前元素的覆盖次数cnt[i]。int sum_d = 0; for (int i = 0; i < n; i++) { sum_d += diff[i]; // sum_d 即为 cnt[i] if (nums[i] > sum_d) return false; // 覆盖次数不足 }
四、代码实现(C++)3355. 零数组变换 I - 力扣(LeetCode)
class Solution {
public:
bool isZeroArray(vector<int>& nums, vector<vector<int>>& queries) {
int n = nums.size();
vector<int> diff(n + 1, 0); // 差分数组,长度 n+1 处理边界
// 处理所有查询,记录区间覆盖次数
for (auto& q : queries) {
int l = q[0], r = q[1];
diff[l]++;
diff[r + 1]--;
}
// 计算前缀和,验证每个元素是否可减为0
int sum_d = 0;
for (int i = 0; i < n; i++) {
sum_d += diff[i]; // 累加得到当前元素的覆盖次数
if (nums[i] > sum_d) return false; // 次数不足,无法变0
}
return true;
}
};
五、关键总结
- 差分数组的本质:
- 用端点标记区间操作的 “开始” 和 “结束”,将 O (n) 区间更新优化为 O (1)。
- 通过前缀和快速还原每个位置的实际变化量。
- 逆向思维:
将 “灵活选择子集减 1” 转化为 “统计覆盖次数”,只需判断nums[i]是否不超过覆盖次数。 - 复杂度优势:
- 处理查询:O (q),计算前缀和:O (n),总时间复杂度 O (n + q),适用于大规模数据。
六、直观示例
输入:nums = [1,0,1], queries = [[0,2]]
- 差分数组处理:
- 查询
[0,2]→diff[0]++,diff[3]--(忽略越界)→diff = [1,0,0,0]。
- 查询
- 前缀和计算:
i=0:sum_d=1→1≥1,合法。i=1:sum_d=1→0≤1,合法。i=2:sum_d=1→1≤1,合法。
输出:true。
七、适用场景
- 频繁的区间加减操作 + 单次整体查询结果。
- 典型问题:数组染色、区间统计、资源分配等。
&spm=1001.2101.3001.5002&articleId=148086594&d=1&t=3&u=9303cc55f37644bca18c838dbbfc284e)
159

被折叠的 条评论
为什么被折叠?



