差分基础概念
差分是一种常用的算法技巧,主要用于高效处理区间更新操作。其核心思想是通过构建差分数组,将区间操作转换为单点操作,从而将时间复杂度从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;
}
优化技巧
- 离散化处理:当数据范围很大但实际数据点较少时,可以先离散化再应用差分。
- 多维差分:三维甚至更高维的差分可以通过类似二维的容斥原理实现。
- 结合其他数据结构:差分数组常与前缀和、线段树等结合使用。
常见错误
- 边界处理:更新区间[r+1]时忘记检查是否越界。
- 初始化问题:差分数组初始值应与原数组对应。
- 还原顺序:必须先处理完所有更新操作后再还原原数组。
扩展练习
- 实现三维差分及其更新操作。
- 解决这个问题:给定一个01矩阵,每次操作翻转一个子矩阵内的所有元素(0变1,1变0),最后统计1的个数。
- 设计一个支持区间加、区间乘、区间查询的数据结构。
差分算法通过巧妙的预处理,将复杂的区间操作简化为高效的单点操作,是算法竞赛和实际工程中处理批量更新的利器。掌握差分技术需要理解其数学原理,并通过大量练习熟悉各种变形和应用场景。
金融领域的应用
在股票或基金分析中,差分用于计算每日价格波动。例如,某股票连续三天的收盘价为 $[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$,触发设备校准指令,防止批量次品。
注意事项
差分对噪声敏感,实际应用中常配合移动平均滤波。高阶差分(如二阶差分)可进一步分析变化率的趋势,但需注意数据平稳性要求。

1万+

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



