题目描述
给你一个由非负整数 a1, a2, …, an 组成的数据流输入,请你将到目前为止看到的数字总结为不相交的区间列表。
实现 SummaryRanges 类:
SummaryRanges() 使用一个空数据流初始化对象。
void addNum(int val) 向数据流中加入整数 val 。
int[][] getIntervals() 以不相交区间 [starti, endi] 的列表形式返回对数据流中整数的总结。
示例:
输入:
[“SummaryRanges”, “addNum”, “getIntervals”, “addNum”, “getIntervals”, “addNum”, “getIntervals”, “addNum”, “getIntervals”, “addNum”, “getIntervals”]
[[], [1], [], [3], [], [7], [], [2], [], [6], []]
输出:
[null, null, [[1, 1]], null, [[1, 1], [3, 3]], null, [[1, 1], [3, 3], [7, 7]], null, [[1, 3], [7, 7]], null, [[1, 3], [6, 7]]]
解释:
SummaryRanges summaryRanges = new SummaryRanges();
summaryRanges.addNum(1); // arr = [1]
summaryRanges.getIntervals(); // 返回 [[1, 1]]
summaryRanges.addNum(3); // arr = [1, 3]
summaryRanges.getIntervals(); // 返回 [[1, 1], [3, 3]]
summaryRanges.addNum(7); // arr = [1, 3, 7]
summaryRanges.getIntervals(); // 返回 [[1, 1], [3, 3], [7, 7]]
summaryRanges.addNum(2); // arr = [1, 2, 3, 7]
summaryRanges.getIntervals(); // 返回 [[1, 3], [7, 7]]
summaryRanges.addNum(6); // arr = [1, 2, 3, 6, 7]
summaryRanges.getIntervals(); // 返回 [[1, 3], [6, 7]]
提示:
0 <= val <= 104
最多调用 addNum 和 getIntervals 方法 3 * 104 次
进阶:如果存在大量合并,并且与数据流的大小相比,不相交区间的数量很小,该怎么办?
解题思路
知识储备
map
map 是 STL 的一个关联容器,它提供一对一的hash
- 第一个称为关键字(key),每个关键字只能在map中出现一次;
- 第二个称为该关键字的值(value);
map以模板(泛型)方式实现,可以存储任意类型的数据,包括使用者自定义的数据类型。Map主要用于资料一对一映射(one-to-one)的情況,map內部的实现自建一颗红黑树,这颗树具有对数据自动排序的功能。在map内部所有的数据都是有序的,后边我们会见识到有序的好处。比如一个班级中,每个学生的学号跟他的姓名就存在著一对一映射的关系。
lower_bound&upper_bound
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。
在从小到大的排序数组中,
- lower_bound(begin,end,num):
从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。 - upper_bound( begin,end,num):
从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
在从大到小的排序数组中,重载lower_bound()和upper_bound()
- lower_bound( begin,end,num,greater() ):
从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。 - upper_bound( begin,end,num,greater() ):
从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
代码
class SummaryRanges {
private:
map<int, int> intervals;
public:
SummaryRanges() {
}
void addNum(int val) {
// 找到 l1 最小的且满足 l1 > val 的区间 interval1 = [l1, r1]
// 如果不存在这样的区间,interval1 为尾迭代器
auto interval1 = intervals.upper_bound(val);
// 找到 l0 最大的且满足 l0 <= val 的区间 interval0 = [l0, r0]
// 在有序集合中,interval0 就是 interval1 的前一个区间
// 如果不存在这样的区间,interval0 为尾迭代器
auto interval0 = (interval1 == intervals.begin() ? intervals.end() : prev(interval1));
//情况一:存在一个区间容纳val
if(interval0!=intervals.end()&&interval0->first<=val&&interval0->second>=val){
return;
}
else{
//是否恰好存在r0+1==val
bool left_aside = (interval0 != intervals.end() && interval0->second + 1 == val);
//是否恰好存在l1-1==val
bool right_aside = (interval1 != intervals.end() && interval1->first - 1 == val);
//情况四,两个同时存在,则合并两个区间
if(left_aside&&right_aside){
int left = interval0->first,right=interval1->second;
intervals.erase(interval0);
intervals.erase(interval1);
intervals.emplace(left,right);
}
//情况二,恰好存在r0+1==val
else if(left_aside){
++(interval0->second);
}
//情况三,恰好存在l1-1==val
else if(right_aside){
int right = interval1->second;
intervals.erase(interval1);
intervals.emplace(val,right);
}
//情况五,加入一个新的区间
else{
intervals.emplace(val,val);
}
}
}
vector<vector<int>> getIntervals() {
vector<vector<int>> ans;
for (const auto& [left, right]: intervals) {
ans.push_back({left, right});
}
return ans;
}
};
/**
* Your SummaryRanges object will be instantiated and called as such:
* SummaryRanges* obj = new SummaryRanges();
* obj->addNum(val);
* vector<vector<int>> param_2 = obj->getIntervals();
*/