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. 数组下标:上述示例中,数组下标从 1 开始,这样更符合差分的数学定义。
  2. 边界处理:修改差分数组时,要注意 r+1 是否越界。
  3. 还原原数组:通过差分数组还原原数组时,需使用前缀和公式:a[i] = a[i-1] + d[i]

7. 练习题

  1. 给定数组 a = [1, 2, 3, 4, 5],对区间 [2, 4] 加 2,区间 [3, 5] 加 3,最后输出修改后的数组。
  2. 设计一个程序,支持多次区间加法操作,并能快速查询任意位置的值。

总结

差分是一种非常实用的预处理技巧,通过将区间修改转化为单点修改,大大提高了处理效率。掌握差分后,很多区间修改问题都能迎刃而解。

1 条评论

  • @ 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 / 牛客网)

    1. 【LeetCode】1893. 检查是否区域内所有整数都满足某个条件 ✅
    2. 【牛客】小明种花问题 🌸
    3. 【洛谷】P5094 【USACO04OPEN】 MooFest 🐄

    📬 结语

    差分是一个非常实用的小技巧,尤其在竞赛和面试中经常出现。希望这份教程能让你轻松入门差分,并掌握它背后的思维方式!

    如果你喜欢这份教程,欢迎点赞、收藏或分享给朋友哦~👋


    📌 作者:XXX
    📅 更新时间:2025年5月13日
    📦 如有疑问,欢迎留言交流!

    • 1