题目
给你区间的 空 集,请你设计并实现满足要求的数据结构:
新增:添加一个区间到这个区间集合中。
统计:计算出现在 至少一个 区间中的整数个数。
实现 CountIntervals 类:CountIntervals() 使用区间的空集初始化对象
void add(int left, int right) 添加区间 [left, right] 到区间集合之中。
int count() 返回出现在 至少一个 区间中的整数个数。
注意:区间 [left, right] 表示满足 left <= x <= right 的所有整数 x 。示例 1:
输入
[“CountIntervals”, “add”, “add”, “count”, “add”, “count”]
[[], [2, 3], [7, 10], [], [5, 8], []]
输出
[null, null, null, 6, null, 8]解释
CountIntervals countIntervals = new CountIntervals(); // 用一个区间空集初始化对象
countIntervals.add(2, 3); // 将 [2, 3] 添加到区间集合中
countIntervals.add(7, 10); // 将 [7, 10] 添加到区间集合中
countIntervals.count(); // 返回 6
// 整数 2 和 3 出现在区间 [2, 3] 中
// 整数 7、8、9、10 出现在区间 [7, 10] 中
countIntervals.add(5, 8); // 将 [5, 8] 添加到区间集合中
countIntervals.count(); // 返回 8
// 整数 2 和 3 出现在区间 [2, 3] 中
// 整数 5 和 6 出现在区间 [5, 8] 中
// 整数 7 和 8 出现在区间 [5, 8] 和区间 [7, 10] 中
// 整数 9 和 10 出现在区间 [7, 10] 中提示:
1 <= left <= right <= 10^9
最多调用 add 和 count 方法 总计 10^5 次
调用 count 方法至少一次来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/count-integers-in-intervals
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
合并区间问题,每次插入一个新的区间时,将和该区间重叠的区间合并。
为快速查找重叠区间,使用一个有序集合保存当前不重叠的区间集合,这里使用TreeMap,key为区间左端点,calue为右端点。map按左端点进行排序。
每次插入新区间[left, right]时,查找map中最大的不大于right的左端点,如果重叠则合并这两个区间,并移除被重叠的区间,区间[left, right]可能和多个区间重叠,因此一直该操作直至无法合并。循环的同时减去相应区间的整数个数,最后加上新的区间个数,更新map。
代码
代码一
class CountIntervals {
int sum;
TreeMap<Integer, Integer> map;
public CountIntervals() {
sum = 0;
map = new TreeMap<>();
}
public void add(int left, int right) {
// 每次循环结束后,left和right分别表示新合并出来的区间的两个端点
Integer l = map.floorKey(right);
// while的条件确保区间重叠
while (l != null && left <= map.get(l)) {
int r = map.get(l);
// 更新合并的区间的端点,left取较小的,right取较大
left = Math.min(l, left);
right = Math.max(right, r);
// sum减去移除的区间整数个数
sum -= r - l + 1;
map.remove(l);
l = map.floorKey(right);
}
sum += right - left + 1;
// map中加入合并后的区间
map.put(left, right);
}
public int count() {
return sum;
}
}
/**
* Your CountIntervals object will be instantiated and called as such:
* CountIntervals obj = new CountIntervals();
* obj.add(left,right);
* int param_2 = obj.count();
*/
代码二
上面的思路是「对于每个新区间[left,right],查找map中不大于right的左端点」。
其实,镜像一下,「查找map中不小于left的右端点」也是可以的,代码如下:
class CountIntervals {
int sum;
TreeMap<Integer, Integer> map;
public CountIntervals() {
sum = 0;
map = new TreeMap<>();
}
public void add(int left, int right) {
Integer r = map.ceilingKey(left);
while (r != null && right >= map.get(r)) {
int l = map.get(r);
left = Math.min(l, left);
right = Math.max(right, r);
sum -= r - l + 1;
map.remove(r);
r = map.ceilingKey(left);
}
sum += right - left + 1;
map.put(right, left);
}
public int count() {
return sum;
}
}
/**
* Your CountIntervals object will be instantiated and called as such:
* CountIntervals obj = new CountIntervals();
* obj.add(left,right);
* int param_2 = obj.count();
*/