LeetCode 715. Range Module
題目要求高效地實現區間的添加,刪除和查詢操作。
題目分析:本題是一道設計題,要求實現一個類(Range Module)的三個方法:addRange(int, int), queryRange(int, int) 和 removeRange(int, int)。
該類的對象會穿插地調用這三個方法。聽起來有點像並查集,但並查集“一般”不涉及刪除操作(不是很確定,只能說“一般”),這道題其實用簡單的map就可以實現。
具體來說:map的key是區間的起點,value是區間的終點(其實用set也可以實現,set的元素是個pair<int, int>,自己實現比較函數即可)。
由於map的key是有序的,所以在添加,刪除或者查詢區間的時候可以用log(n)的時間快速定位到用執行區間操作的位置。
在每次進行區間操作(add, remove or query)之前,區間與區間之間都沒有重疊部分,且區間與區間不相鄰。
addRange(int left, int right):如果對象裏沒有任何區間,則直接插入要加的區間即可。否則,利用map<int, int>的lower_bound(int)函數查找第一個左區間大於或者等於left位置iter(時間複雜度是log(n)),接下來分三種情況:
- 如果iter == map::end,說明所有左區間都小於left(包括最後一個區間),此時a)如果最後一個區間的右區間小於left,直接插入
[left, right)
到末尾即可;b)否則,將最後一個區間的右區間只改爲右區間和right中較大的一個即可。 - 如果iter == map::begin,說明在iter之前沒有任何區間,此時從iter開始迭代區間,直到iter == map::end 或者iter的左區間大於left。迭代過程中如果發現當前區間與
[left, right)
沒有交集且不相鄰,則跳過此區間,如果相交或者相鄰,則將left賦爲left和當前左區間中較小的一個,right賦爲right和當前右區間較大的一個,並刪除當前區間。迭代結束之後將[left, right)
加入到map中。 - iter在除以上兩種情況外的位置,此時iter前的一個區間可能與
[left, right)
也有交集,所以先將iter–,然後在執行步驟2的操作即可。
removeRange(int left, int right):如果對象裏沒有任何區間,直接返回。 否則,利用map<int, int>的lower_bound(int)函數查找第一個左區間大於或者等於left位置iter(時間複雜度是log(n)),接下來分三種情況:
- 如果iter == map::end,說明所有左區間都小於left(包括最後一個區間),此時a)如果最後一個區間的右區間小於等於left,直接返回即可;b)如果最後一個區間的右區間小於等於right,只需將最後一個右區間的改爲left即可;c)如果最後一個區間的右區間小於right,除了將最後一個右區間的改爲left,還有插入
[right, 最後一個區間的右區間)
到對象中。 - 如果iter == map::begin,說明在iter之前沒有任何區間,此時從iter開始迭代區間,直到iter == map::end 或者 iter的左區間大於等於left。迭代過程中如果發現當前區間與
[left, right)
沒有交集,則跳過此區間,如果相交,可分爲以下四種情況:(當前區間的左區間用first表示,當前區間的右區間用second表示)
a) 如果first <= left && second > right,插入[second, right)
當對象中,並刪除當前區間;
b) 如果first <= left && second <= right,刪除當前區間即可;
c) 如果first > left && second <= right,將當前區間的右區間改爲left即可;
d) 如果first > left && second > right,將[right, second)
插入對象中,並將當前區間的右區間值改爲left。 - iter在除以上兩種情況外的位置,此時iter前的一個區間可能與
[left, right)
也有交集,所以先將iter–,然後在執行步驟2的操作即可。
queryRange(int left, int right):如果對象裏沒有任何區間,返回false。否則,利用map<int, int>的lower_bound(int)函數查找第一個左區間大於或者等於left位置iter(時間複雜度是log(n)),接下來分三種情況:
- 如果iter == map::end,說明所有左區間都小於left(包括最後一個區間),此時只需考察最後一個區間的右區間是否大於等於right,是則返回true,否則返回false;
- 如果iter == map::begin,情況與1相同,只需考察第一個區間的右區間是否大於等於right,是則返回true,否則返回false;
- iter在除以上兩種情況外的位置,此時iter前的一個區間可能與
[left, right)
也有交集,除了考察當前區間是否包含[left, right)
以外, 還有考察iter的前一個區間是否包含[left, right)
。
class RangeModule {
map<int, int> range;
public:
RangeModule() {
}
void addRange(int left, int right) {
if(range.empty()) {
range.insert(make_pair(left, right));
return;
}
auto iter = range.lower_bound(left);
if(iter == range.end()) {
auto end = range.rbegin();
int a = end -> first, b = end -> second;
if(b < left) {
range.insert(make_pair(left, right));
} else {
range[a] = max(right, b);
}
} else {
if(iter != range.begin()) iter --;
while(iter != range.end() && iter -> first <= right) {
if(iter -> second < left) {
iter ++;
continue;
}
left = min(left, iter -> first);
right = max(right, iter -> second);
iter = range.erase(iter);
}
range.insert(make_pair(left, right));
}
}
bool queryRange(int left, int right) {
if(range.empty()) return false;
auto iter = range.lower_bound(left);
if(iter == range.end()) return right <= range.rbegin() -> second;
if(left >= iter -> first && right <= iter -> second) return true;
if(iter == range.begin()) return false;
iter --;
return left >= iter -> first && right <= iter -> second;
}
void removeRange(int left, int right) {
if(range.empty()) return;
auto iter = range.lower_bound(left);
if(iter == range.end()) {
auto end = range.rbegin();
int a = end -> first, b = end -> second;
if(b <= left) return;
if(end -> second > right) {
range[right] = b;
}
range[a] = left;
} else {
if(iter != range.begin()) iter --;
while(iter != range.end() && iter -> first < right) {
if(iter -> second <= left) {
iter ++;
continue;
}
if(iter -> first >= left) {
if(iter -> second > right) {
range.insert(make_pair(right, iter -> second));
}
iter = range.erase(iter);
} else {
if(iter -> second > right) {
range.insert(make_pair(right, iter -> second));
}
range[iter -> first] = left;
iter ++;
}
}
}
}
};