浅谈差分
当我们只做区间修改和单点查询时,差分这个数据结构就非常有用,不用去写线段树和树状数组。
差分数组是什么?
就是个数组,就是个辅助数组
我数据结构没学好深,谈谈自己对差分的理解
差分就是数据之间的差,就是原始数组相邻元素之间的差值 diff[ i ] = a[ i + 1] - a[ i ]
下面给出一个例子:
当我们进行区间修改时:
- [1 4] 加2
- [2 4] 减2
看着很简单把,我直接暴力循环都能写完,但是当区间达到1e5是就会超时
这个时候差分数组的优点就体现出来了,其实你对整个区间进行修改他们的差分是不会变的
有了这个想法 我们进行第一步[1 4] 加2 其实diff[ 1 ] 和 diff[ 5 ]变了,diff[ 2, 3, 4 ]是没有变的
第二步是一样的
看了这个例子规律就出来了当我们对区间修改时差分数组只有端点的值改变了
也就是我们对区间[ l r ] 改变a也就是
diff[ l ] += a; diff[r + 1] -= a;
while(m--) {//m操作次数
cin>>l>>r>>change;//l r为端点 change为修改的值
diff[l] += change;
diff[r + 1] -= change;
}
前面还说了差分数组的一个大用处就是单点查询
当我们需要知道改变后的一个值时怎么办
这很简单a[ i ] 就等于diff[0…i]相加
当然也可等于前一个数加上diff[ i ];
int sum = 0;
for(int i = 0; i < len; ++i) {
sum += diff[i];
a[i] = sum;
}
for(int i = 0; i < len; ++i) {
a[i] = a[i - 1] + diff[i];
}
来一道例题:
https://leetcode-cn.com/problems/minimum-moves-to-make-array-complementary/
1674. 使数组互补的最少操作次数
给你一个长度为 偶数 n 的整数数组 nums 和一个整数 limit 。每一次操作,你可以将 nums 中的任何整数替换为 1 到 limit 之间的另一个整数。
如果对于所有下标 i(下标从 0 开始),nums[i] + nums[n - 1 - i] 都等于同一个数,则数组 nums 是 互补的 。例如,数组 [1,2,3,4] 是互补的,因为对于所有下标 i ,nums[i] + nums[n - 1 - i] = 5 。
返回使数组 互补 的 最少 操作次数。
示例 1:
输入:nums = [1,2,4,3], limit = 4
输出:1
解释:经过 1 次操作,你可以将数组 nums 变成 [1,2,2,3](加粗元素是变更的数字):
nums[0] + nums[3] = 1 + 3 = 4.
nums[1] + nums[2] = 2 + 2 = 4.
nums[2] + nums[1] = 2 + 2 = 4.
nums[3] + nums[0] = 3 + 1 = 4.
对于每个 i ,nums[i] + nums[n-1-i] = 4 ,所以 nums 是互补的。
示例 2:
输入:nums = [1,2,2,1], limit = 2
输出:2
解释:经过 2 次操作,你可以将数组 nums 变成 [2,2,2,2] 。你不能将任何数字变更为 3 ,因为 3 > limit 。
示例 3:
输入:nums = [1,2,1,2], limit = 2
输出:0
解释:nums 已经是互补的。
提示:
n == nums.length
2 <= n <= 105
1 <= nums[i] <= limit <= 105
n 是偶数。
是力扣上的一道题:
问我们形成互补数组最少的操作数
我们定义一个cnt[]数组表示cnt[ x ] 互补为x需要的操作数,最后循环cnt数组取最小值便是答案
有了这个思路 关键是我们怎么得出互补为x 需要的操作数
为了方便下面就用 a表示nums[ i ] b表示nums[ len - i - 1]
所以:
如果修改后是 a + b 那么需要的操作为0
如果修改后是 [ 1 + min(a,b), limit + max(a,b)] 那么需要的操作为1
如果修改后是 [2, 2 * limit] 那么需要的操作为2
我们的算法就是跑一遍 len / 2 个组
对 [2, 2 * limit] 区间加2
对[ 1 + min(a,b), limit + max(a,b)] 区间减1
对[a + b, a + b ] 区间减1
这样我们就能的到差分数组了;再使用上面差分数组的方法
class Solution {
public:
int minMoves(vector<int>& nums, int limit) {
int ans = 1e5 + 5;
int len = nums.size();
int len2 = len / 2;
vector<int> cnt(2*limit + 5,0);
for(int i = 0; i < len2; ++i) {
int a = nums[i];
int b = nums[len - i - 1];
int l = 2;
int r = 2 * limit;
cnt[l] += 2;
cnt[r + 1] -= 2;
l = 1 + min(a,b);
r = limit + max(a,b);
cnt[l] += -1;
cnt[r + 1] -= -1;
l = a + b;
r = a + b;
cnt[l] += -1;
cnt[r + 1] -= -1;
}
int sum = 0;
for(int i = 2; i <= 2 * limit; ++i) {
sum += cnt[i];
ans = min(ans,sum);
}
return ans;
}
};