今天刷题,看到打点计数器,虽然高中物理忘得差不多了,但是看到这个东西还是比较亲切,题目比较简单,给出几个整数区间,统计区间覆盖的整数个数,这道题印象当中我是在哪里见过类似的题目,脑海中立刻想到差分数组(还好不是立刻想到线段树)。
所谓差分数组,是怎么说来着?没关系,查一下:
差分数组通常是指一个数组,其中每个元素是原数组中对应元素与前一个元素的差。这种数组在处理序列数据时非常有用,尤其是在需要计算连续项之间的变化或者进行数据压缩时。
应用场景:
1.数据压缩:减少存储空间,特别是在数据变化不大时。
2.快速求和:快速计算数组中任意一段区间的和。
3.更新和查询:在数组中快速更新值并查询特定统计信息。
4.游戏开发:处理游戏中角色位置等动态变化的数据。
5.信号处理:在音频或视频编辑中表示信号的变化。
本题,就是一个差分数组的一个最典型的应用,在这之前,我们先来分析一下暴力解法的时间复杂度:
每处理一个区间,需要遍历这个区间内所有的数,对他们统计出现次数或是标记已出现过。假设有 n 个区间,每个区间的平均长度为 l。在最坏的情况下,所有区间都是连续且没有重叠的,那么总的遍历次数将是所有区间长度之和,即 O(nl)。如果考虑区间可能有重叠的情况,实际的时间复杂度可能会略低。
假如采用线段树(本题并不推荐),情况会有所不同,构建线段树的时间复杂度是 O(n),其中 n 是整个数值范围的大小。每次处理一个区间,时间复杂度为 O(logn)。如果有 m 个区间需要处理,那么总的时间复杂度为 O(mlogn+n)。这里 m 表示区间数量,n 表示数值范围大小。最终查询结果时间复杂度为 O(1)。
进入正题,使用差分数组,对于每个区间,只需要对数组进行如下操作:区间左端点 l 加一,右端点 r+1 减一,时间复杂度为 O(m),其中m 为区间个数,最终只需要累加最小和最大值之间所有的差分值,累加的过程中,如果累加值大于0,表示这个位置是包含在至少一个区间之内的,即该位置是被打点的。统计打点数的过程时间复杂度为 O(n),其中 n 为区间内数据量。
没理解?没关系,Talk is cheap, show you my code!
public static int solution(int[][] inputArray) {
int mx = 0;
for (int[] range : inputArray) {
mx = Math.max(mx, range[1]);
}
int[] diff = new int[mx + 2];
for (int[] range : inputArray) {
diff[range[0]]++;
diff[range[1] + 1]--;
}
int cnt = 0, flag = 0;
for (int i = 0; i <= mx; i++) {
flag += diff[i];
if (flag > 0) {
cnt++;
}
}
return cnt;
}
本题作为差分数组的典型,适合初学差分数组的朋友细细理解,我当初第一次接触也是拍案叫绝,惊叹于人类的智慧,哈哈。