- C++
C++ 差分学习笔记
- 2025-5-13 20:27:25 @
C++ 差分教程
差分是一种常用的数学方法,用于高效处理区间修改问题。在算法竞赛和实际应用中经常会用到。下面我将详细介绍差分的概念、原理和使用方法。
1. 什么是差分?
差分是一种数据结构,用于高效处理对数组的区间修改操作。
假设有一个数组 a = [a₁, a₂, a₃, ..., aₙ]
,我们可以构造一个差分数组 d
,使得:
d[1] = a[1]
d[i] = a[i] - a[i-1]
(i ≥ 2)
简单来说,差分数组记录了原数组中每个元素与前一个元素的差值。
2. 差分数组的作用
差分数组的主要作用是:将区间修改操作转化为单点修改操作,从而将时间复杂度从 O(n) 降为 O(1)。
例如,要对原数组 a
的区间 [l, r]
内的所有元素加上一个值 k
,只需要在差分数组 d
上进行以下操作:
d[l] += k
d[r+1] -= k
(如果r+1 ≤ n
)
最后,通过差分数组还原出原数组时,区间 [l, r]
内的元素都会被加上 k
,而其他元素不受影响。
3. 如何构造差分数组?
假设原数组为 a
,差分数组为 d
,构造方法如下:
#include <iostream>
#include <vector>
using namespace std; // 使用标准命名空间,避免重复写std::
int main() {
// 原数组(下标从1开始)
vector<int> a = {0, 3, 5, 7, 2, 9}; // 注意:a[0] 不使用,从 a[1] 开始
int n = a.size() - 1; // 数组长度
// 构造差分数组
vector<int> d(n + 2, 0); // 差分数组长度为 n+2,避免越界
for (int i = 1; i <= n; i++) {
d[i] = a[i] - a[i-1];
}
// 输出差分数组
cout << "差分数组 d: ";
for (int i = 1; i <= n; i++) {
cout << d[i] << " ";
}
cout << endl;
return 0;
}
输出结果:
差分数组 d: 3 2 2 -5 7
4. 如何进行区间修改?
假设要对原数组 a
的区间 [2, 4]
内的所有元素加上 3
,只需修改差分数组:
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 原数组
vector<int> a = {0, 3, 5, 7, 2, 9};
int n = a.size() - 1;
// 构造差分数组
vector<int> d(n + 2, 0);
for (int i = 1; i <= n; i++) {
d[i] = a[i] - a[i-1];
}
// 区间 [2, 4] 加 3
int l = 2, r = 4, k = 3;
d[l] += k;
if (r + 1 <= n) d[r+1] -= k;
// 通过差分数组还原原数组
vector<int> new_a(n + 1, 0);
new_a[1] = d[1];
for (int i = 2; i <= n; i++) {
new_a[i] = new_a[i-1] + d[i];
}
// 输出修改后的原数组
cout << "修改后的原数组: ";
for (int i = 1; i <= n; i++) {
cout << new_a[i] << " ";
}
cout << endl;
return 0;
}
输出结果:
修改后的原数组: 3 8 10 5 9
5. 差分的应用场景
差分常用于处理频繁的区间修改问题,例如:
- 多次对数组的不同区间进行加法操作,最后查询数组的某个位置或整体状态。
- 线段树、树状数组等高级数据结构的基础。
- 游戏开发中的技能范围伤害计算。
6. 注意事项
- 数组下标:上述示例中,数组下标从 1 开始,这样更符合差分的数学定义。
- 边界处理:修改差分数组时,要注意
r+1
是否越界。 - 还原原数组:通过差分数组还原原数组时,需使用前缀和公式:
a[i] = a[i-1] + d[i]
。
7. 练习题
- 给定数组
a = [1, 2, 3, 4, 5]
,对区间[2, 4]
加 2,区间[3, 5]
加 3,最后输出修改后的数组。 - 设计一个程序,支持多次区间加法操作,并能快速查询任意位置的值。
总结
差分是一种非常实用的预处理技巧,通过将区间修改转化为单点修改,大大提高了处理效率。掌握差分后,很多区间修改问题都能迎刃而解。
1 条评论
-
admin SU @ 2025-5-13 20:27:59
🌟C++ 差分算法教程(0基础也能懂)🌟
📚 什么是差分?
差分是一种对数组进行预处理的技巧,常用于:
- 快速实现区间增减操作(比如:对一个数组的某个区间的所有元素同时加一个数)
- 高效地多次执行这样的操作
- 最后再通过一次计算得到最终的数组结果
💡 类比理解:想象你在画一条折线图,差分就像是记录相邻两个点之间的“变化量”,而不是原始数值。
🔁 差分的核心思想
给定一个原始数组
a[0...n]
,我们构造一个差分数组d[0...n+1]
,使得:d[0] = a[0] d[i] = a[i] - a[i-1] (i > 0)
反过来,原始数组可以通过差分数组还原:
a[0] = d[0] a[i] = a[i-1] + d[i]
🧠 差分的作用
如果我们想对
a[L] ~ a[R]
这个区间内的所有元素都加上一个数k
,使用差分可以在 O(1) 的时间复杂度内完成这个操作:d[L] += k; if (R + 1 <= n) d[R + 1] -= k;
这样,当我们最后还原数组时,就能得到正确的修改后的结果!
✅ 示例讲解
原始数组:
a = [1, 3, 5, 7, 9]
构造差分数组:
d[0] = 1 d[1] = 3 - 1 = 2 d[2] = 5 - 3 = 2 d[3] = 7 - 5 = 2 d[4] = 9 - 7 = 2
所以差分数组是:
d = [1, 2, 2, 2, 2]
🛠️ C++ 实现代码(详细注释)
使用
using namespace std;
来简化代码,适合初学者学习和理解。#include <iostream> #include <vector> using namespace std; // 使用标准命名空间,避免重复写std:: int main() { // 原始数组 vector<int> a = {1, 3, 5, 7, 9}; int n = a.size(); vector<int> d(n + 2, 0); // 初始化差分数组,大小为n+2是为了方便处理边界 // 构造差分数组 d[0] = a[0]; for (int i = 1; i < n; ++i) { d[i] = a[i] - a[i - 1]; } cout << "差分数组 d: "; for (int i = 0; i < n; ++i) { cout << d[i] << " "; } cout << endl; // 对 a[1] 到 a[3] 区间加上 2(即索引从1到3) int L = 1, R = 3, k = 2; d[L] += k; if (R + 1 < n) d[R + 1] -= k; // 还原数组 vector<int> res(n); res[0] = d[0]; for (int i = 1; i < n; ++i) { res[i] = res[i - 1] + d[i]; } cout << "还原后的数组: "; for (int num : res) { cout << num << " "; } cout << endl; return 0; }
📈 输出示例
运行上面的程序会输出:
差分数组 d: 1 2 2 2 2 还原后的数组: 1 5 7 9 9
可以看到,原来的
3 → 5
,5 → 7
,7 → 9
都增加了 2,而9
没有改变。
🧩 总结知识点
操作 时间复杂度 说明 构造差分数组 O(n) 只需要遍历一次数组 区间增减操作 O(1) 修改两个位置即可 还原数组 O(n) 再次遍历差分数组
🧑🏫 小贴士(适合新手)
- 差分适用于频繁对数组某段区间做加减操作的场景。
- 差分数组长度通常设置为
n+2
,防止越界。 - 如果你学过前缀和,差分可以看作是它的“反向操作”。
📖 扩展练习题(来自 LeetCode / 牛客网)
- 【LeetCode】1893. 检查是否区域内所有整数都满足某个条件 ✅
- 【牛客】小明种花问题 🌸
- 【洛谷】P5094 【USACO04OPEN】 MooFest 🐄
📬 结语
差分是一个非常实用的小技巧,尤其在竞赛和面试中经常出现。希望这份教程能让你轻松入门差分,并掌握它背后的思维方式!
如果你喜欢这份教程,欢迎点赞、收藏或分享给朋友哦~👋
📌 作者:XXX
📅 更新时间:2025年5月13日
📦 如有疑问,欢迎留言交流!
- 1