题目:
A Range Module is a module that tracks ranges of numbers. Your task is to design and implement the following interfaces in an efficient manner.
-
addRange(int left, int right)
Adds the half-open interval[left, right)
, tracking every real number in that interval. Adding an interval that partially overlaps with currently tracked numbers should add any numbers in the interval[left, right)
that are not already tracked. -
queryRange(int left, int right)
Returns true if and only if every real number in the interval[left, right)
is currently being tracked. -
removeRange(int left, int right)
Stops tracking every real number currently being tracked in the interval[left, right)
.Example 1:
addRange(10, 20): null removeRange(14, 16): null queryRange(10, 14): true (Every number in [10, 14) is being tracked) queryRange(13, 15): false (Numbers like 14, 14.03, 14.17 in [13, 15) are not being tracked) queryRange(16, 17): true (The number 16 in [16, 17) is still being tracked, despite the remove operation)
Note:
-
A half open interval
[left, right)
denotes all real numbersleft <= x < right
. -
0 < left < right < 10^9
in all calls toaddRange, queryRange, removeRange
. -
The total number of calls to
addRange
in a single test case is at most1000
. -
The total number of calls to
queryRange
in a single test case is at most5000
. -
The total number of calls to
removeRange
in a single test case is at most1000
.思路:
本题目可以用线段树来实现,但是这里我们用二分查找树来实现。我们定义一个map<int, int> invals,用来表示互不相交的线段,例如invales[left] = right就可以表示一个左闭右开的区间[left, right)。下面就看看各个函数分别怎么实现:
1)addRange(int left, int right):首先找出[left, right)的前一个interval,然后判断我们是否需要在插入interval之前合并已有的interval,如果需要,则首先将待合并的interval删除,并更新left和right,最后将[left, right)插入二分查找树中。该函数的时间复杂度是O(nlogn)。2)queryRange(int left, int right):首先找出小于等于[left, right)的一个interval,然后判断它的右边是否覆盖掉了[left, right),如果是,则返回false,否则说明该interval覆盖掉了[left, right),所以返回true。该函数的时间复杂度仍然是O(nlogn)。
3)removeRange(int left, int right):和addRange类似,我们首先找出和[left, right)重合的interval(s),然后将该区间内的interval(s)都移除掉。但是需要注意的是,如果左边有残余的interval或者右边有残余的interval,则需要分别将它们保留。该函数的时间复杂度仍然是O(nlogn)。
整个算法的空间复杂度是O(n)。
代码:
class RangeModule { public: RangeModule() { } void addRange(int left, int right) { auto l = invals.upper_bound(left), r = invals.upper_bound(right); if (l != invals.begin()) { // find the iterator that is before the new segment l--; if (l->second < left) { l++; } } if (l != r) { // if l != r, we should merge the intervals left = min(left, l->first); right = max(right, (--r)->second); invals.erase(l,++r); } invals[left] = right; } bool queryRange(int left, int right) { auto it = invals.upper_bound(left); if (it == invals.begin() || (--it)->second < right) { return false; } return true; } void removeRange(int left, int right) { auto l = invals.upper_bound(left), r = invals.upper_bound(right); if (l != invals.begin()) { l--; if (l->second < left) { l++; } } if (l == r) { // no interval to remove return; } int l1 = min(left, l->first), r1 = max(right, (--r)->second); invals.erase(l, ++r); if (l1 < left) { // a smaller interval left in the left invals[l1] = left; } if (r1 > right) { // a smaller interval left in the right invals[right] = r1; } } private: map<int, int> invals; }; /** * Your RangeModule object will be instantiated and called as such: * RangeModule obj = new RangeModule(); * obj.addRange(left,right); * bool param_2 = obj.queryRange(left,right); * obj.removeRange(left,right); */