C++差分全解

差分基础概念

差分是一种常用的算法技巧,主要用于高效处理区间更新操作。其核心思想是通过构建差分数组,将区间操作转换为单点操作,从而将时间复杂度从O(n)降低到O(1)。

差分数组的定义:对于原数组a,其差分数组d满足d[i] = a[i] - a[i-1](i > 0),d[0] = a[0]。通过差分数组,可以快速实现对原数组某个区间的增减操作。

一维差分实现

构建差分数组:

vector<int> buildDiff(vector<int>& a) {
    int n = a.size();
    vector<int> d(n);
    d[0] = a[0];
    for (int i = 1; i < n; ++i) {
        d[i] = a[i] - a[i-1];
    }
    return d;
}

区间更新操作:

void updateRange(vector<int>& d, int l, int r, int val) {
    d[l] += val;
    if (r + 1 < d.size()) {
        d[r+1] -= val;
    }
}

还原原数组:

vector<int> restore(vector<int>& d) {
    vector<int> a(d.size());
    a[0] = d[0];
    for (int i = 1; i < d.size(); ++i) {
        a[i] = a[i-1] + d[i];
    }
    return a;
}

二维差分实现

二维差分用于处理矩阵的区间更新。定义差分矩阵d[i][j]表示从(0,0)到(i,j)矩形区域的增量。

构建差分矩阵:

vector<vector<int>> buildDiff2D(vector<vector<int>>& matrix) {
    int m = matrix.size(), n = matrix[0].size();
    vector<vector<int>> d(m, vector<int>(n, 0));
    d[0][0] = matrix[0][0];
    for (int i = 1; i < m; ++i) d[i][0] = matrix[i][0] - matrix[i-1][0];
    for (int j = 1; j < n; ++j) d[0][j] = matrix[0][j] - matrix[0][j-1];
    for (int i = 1; i < m; ++i) {
        for (int j = 1; j < n; ++j) {
            d[i][j] = matrix[i][j] - matrix[i-1][j] - matrix[i][j-1] + matrix[i-1][j-1];
        }
    }
    return d;
}

子矩阵更新:

void updateRegion(vector<vector<int>>& d, int x1, int y1, int x2, int y2, int val) {
    d[x1][y1] += val;
    if (x2 + 1 < d.size()) d[x2+1][y1] -= val;
    if (y2 + 1 < d[0].size()) d[x1][y2+1] -= val;
    if (x2 + 1 < d.size() && y2 + 1 < d[0].size()) d[x2+1][y2+1] += val;
}

还原原矩阵:

vector<vector<int>> restore2D(vector<vector<int>>& d) {
    int m = d.size(), n = d[0].size();
    vector<vector<int>> mat(m, vector<int>(n));
    mat[0][0] = d[0][0];
    for (int i = 1; i < m; ++i) mat[i][0] = mat[i-1][0] + d[i][0];
    for (int j = 1; j < n; ++j) mat[0][j] = mat[0][j-1] + d[0][j];
    for (int i = 1; i < m; ++i) {
        for (int j = 1; j < n; ++j) {
            mat[i][j] = mat[i-1][j] + mat[i][j-1] - mat[i-1][j-1] + d[i][j];
        }
    }
    return mat;
}

典型题目解析

题目1:区间加法

给定一个长度为n的数组,初始全为0。进行k次操作,每次给区间[l,r]增加val。最后输出整个数组。

解法:

vector<int> getModifiedArray(int length, vector<vector<int>>& updates) {
    vector<int> d(length, 0);
    for (auto& update : updates) {
        int l = update[0], r = update[1], val = update[2];
        d[l] += val;
        if (r + 1 < length) d[r+1] -= val;
    }
    vector<int> res(length);
    res[0] = d[0];
    for (int i = 1; i < length; ++i) {
        res[i] = res[i-1] + d[i];
    }
    return res;
}

题目2:航班预订统计

有n个航班,编号1到n。给定 bookings = [[1,2,10],[2,3,20],[2,5,25]],表示从航班1到2各预定10个座位,航班2到3各预定20个座位等。返回每个航班的预定总数。

解法:

vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
    vector<int> d(n + 2, 0);
    for (auto& b : bookings) {
        d[b[0]] += b[2];
        d[b[1]+1] -= b[2];
    }
    vector<int> res(n);
    res[0] = d[1];
    for (int i = 1; i < n; ++i) {
        res[i] = res[i-1] + d[i+1];
    }
    return res;
}

题目3:子矩阵求和

给定一个m×n矩阵,处理两种操作:1. 更新子矩阵(x1,y1)到(x2,y2)所有元素加val;2. 查询子矩阵和。

解法:

class NumMatrix {
    vector<vector<int>> diff;
    vector<vector<int>> mat;
    int m, n;
public:
    NumMatrix(vector<vector<int>>& matrix) {
        m = matrix.size(), n = matrix[0].size();
        mat = vector<vector<int>>(m, vector<int>(n, 0));
        diff = vector<vector<int>>(m + 1, vector<int>(n + 1, 0));
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                updateRegion(i, j, i, j, matrix[i][j]);
            }
        }
    }
    
    void updateRegion(int x1, int y1, int x2, int y2, int val) {
        diff[x1][y1] += val;
        diff[x2+1][y1] -= val;
        diff[x1][y2+1] -= val;
        diff[x2+1][y2+1] += val;
    }
    
    int sumRegion(int x1, int y1, int x2, int y2) {
        return mat[x2][y2] - (x1>0?mat[x1-1][y2]:0) - (y1>0?mat[x2][y1-1]:0) + (x1>0&&y1>0?mat[x1-1][y1-1]:0);
    }
    
    void update() {
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                mat[i][j] = (i>0?mat[i-1][j]:0) + (j>0?mat[i][j-1]:0) - (i>0&&j>0?mat[i-1][j-1]:0) + diff[i][j];
            }
        }
    }
};

高阶应用

题目4:最大重叠区间数

给定一组区间,找出被最多区间覆盖的点。例如[[1,5],[2,6],[3,4]],返回3(点3、4都被3个区间覆盖)。

解法:

int maxOverlap(vector<vector<int>>& intervals) {
    map<int, int> diff;
    for (auto& inv : intervals) {
        diff[inv[0]]++;
        diff[inv[1]+1]--;
    }
    int res = 0, cur = 0;
    for (auto& p : diff) {
        cur += p.second;
        res = max(res, cur);
    }
    return res;
}

题目5:会议室II

给定一组会议时间区间,问至少需要多少会议室。例如[[0,30],[5,10],[15,20]],返回2。

解法:

int minMeetingRooms(vector<vector<int>>& intervals) {
    map<int, int> diff;
    for (auto& inv : intervals) {
        diff[inv[0]]++;
        diff[inv[1]]--;
    }
    int res = 0, cur = 0;
    for (auto& p : diff) {
        cur += p.value;
        res = max(res, cur);
    }
    return res;
}

优化技巧

  1. 离散化处理:当数据范围很大但实际数据点较少时,可以先离散化再应用差分。
  2. 多维差分:三维甚至更高维的差分可以通过类似二维的容斥原理实现。
  3. 结合其他数据结构:差分数组常与前缀和、线段树等结合使用。

常见错误

  1. 边界处理:更新区间[r+1]时忘记检查是否越界。
  2. 初始化问题:差分数组初始值应与原数组对应。
  3. 还原顺序:必须先处理完所有更新操作后再还原原数组。

扩展练习

  1. 实现三维差分及其更新操作。
  2. 解决这个问题:给定一个01矩阵,每次操作翻转一个子矩阵内的所有元素(0变1,1变0),最后统计1的个数。
  3. 设计一个支持区间加、区间乘、区间查询的数据结构。

差分算法通过巧妙的预处理,将复杂的区间操作简化为高效的单点操作,是算法竞赛和实际工程中处理批量更新的利器。掌握差分技术需要理解其数学原理,并通过大量练习熟悉各种变形和应用场景。

金融领域的应用

在股票或基金分析中,差分用于计算每日价格波动。例如,某股票连续三天的收盘价为 $[100, 105, 102]$ 元,一阶差分为 $[5, -3]$,反映涨跌幅度。高频交易中,差分可帮助识别短期趋势。

气象数据监测

气象站通过差分处理每小时温度数据。若某日温度记录为 $[22, 24, 23, 21]$℃,差分序列 $[2, -1, -2]$ 显示温度变化速率,预警突发降温。

运动健康追踪

智能手环利用差分计算步频。假设每分钟步数序列为 $[60, 65, 70]$,差分为 $[5, 5]$,表明用户加速行走。结合加速度传感器数据,可优化卡路里消耗模型。

工业生产质量控制

生产线记录每小时零件尺寸偏差 $[0.1, 0.12, 0.09]$ mm,差分为 $[0.02, -0.03]$。若差分绝对值超过阈值 $0.05$,触发设备校准指令,防止批量次品。

注意事项

差分对噪声敏感,实际应用中常配合移动平均滤波。高阶差分(如二阶差分)可进一步分析变化率的趋势,但需注意数据平稳性要求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨染千千秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值