题目来源:https://leetcode-cn.com/problems/data-stream-as-disjoint-intervals/
大致题意:
给定一组操作,第一个是分割区间初始化,接下来可能是像当前的分割区间中插入一个数(插入可能导致已有区间的变化),也可能是输出当前的分割区间
思路
当初始化后,插入单个数可以理解为插入单个长度为 1 的区间,但是若新插入的区间与已有的区间相邻,那么就需要进行区间合并,当然新插入的区间有可能在已有的区间范围内,这样也就不会生成新的分割区间。
那么,新插入的数对已有区间的影响可能为:
- 插入一个新区间
- 导致一个区间的扩大,而区间扩大后有可能与另一个区间相邻,因而也有可能导致区间合并
- 没有影响
假设:新插入的数 val 左边最近的区间为 [l0, r0](这里保证 l0 <= val),右边最近的区间为[l1, r1](这里保证 l1 > val),那么有以下情况:
- l0 <= val <= r0,区间不变化
- r0 + 1 = val,左区间向右扩大一位
- l1 - 1 = val,右区间向左扩大一位
- ro + 1 = l1 - 1 = val,左右两区间合并
- val > r0 且 val < l1,区间无变化
那么每次插入 val 后,就可以通过判断它与左右区间的关系来进行相应的操作
有序集合
于是可以使用有序集合来保存当前有的区间:将区间左边界作为 key,右边界作为 value
有序集合也可以查找小于某个数中最大的 key 对应 key-value 对 和 大于某个数中最小的 key 对应 key-value 对,也就是刚刚提到的左区间和右区间
代码:
public class SummaryRanges {
private TreeMap<Integer, Integer> intervals;
public SummaryRanges() {
intervals = new TreeMap<>();
}
public void addNum(int val) {
// 找 最大的 左边界小于等于 val 的区间
Map.Entry<Integer, Integer> intervals0 = intervals.floorEntry(val);
// 找 最小的 左边界大于 val 的区间
Map.Entry<Integer, Integer> intervals1 = intervals.ceilingEntry(val + 1);
// 情况一
if (intervals0 != null && intervals0.getKey() <= val && intervals0.getValue() >= val) {
return;
}
else {
boolean leftSide = intervals0 != null && intervals0.getValue() + 1 == val;
boolean rightSide = intervals1 != null && intervals1.getKey() - 1 == val;
// 情况四
if (leftSide && rightSide) {
intervals.put(intervals0.getKey(), intervals1.getValue());
intervals.remove(intervals1.getKey());
}
// 情况二
else if (leftSide) {
intervals.put(intervals0.getKey(), val);
}
// 情况三
else if (rightSide) {
intervals.put(val, intervals1.getValue());
intervals.remove(intervals1.getKey());
}
// 情况五
else {
intervals.put(val, val);
}
}
}
public int[][] getIntervals() {
int n = intervals.size();
int[][] ans = new int[n][2];
int idx = 0;
for (Map.Entry<Integer, Integer> entry : intervals.entrySet()) {
ans[idx][0] = entry.getKey();
ans[idx++][1] = entry.getValue();
}
return ans;
}
}