【原創】715. Range Module -- Leetcode 算法筆記

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)),接下來分三種情況:

  1. 如果iter == map::end,說明所有左區間都小於left(包括最後一個區間),此時a)如果最後一個區間的右區間小於left,直接插入[left, right)到末尾即可;b)否則,將最後一個區間的右區間只改爲右區間和right中較大的一個即可。
  2. 如果iter == map::begin,說明在iter之前沒有任何區間,此時從iter開始迭代區間,直到iter == map::end 或者iter的左區間大於left。迭代過程中如果發現當前區間與[left, right)沒有交集且不相鄰,則跳過此區間,如果相交或者相鄰,則將left賦爲left和當前左區間中較小的一個,right賦爲right和當前右區間較大的一個,並刪除當前區間。迭代結束之後將[left, right)加入到map中。
  3. iter在除以上兩種情況外的位置,此時iter前的一個區間可能與[left, right)也有交集,所以先將iter–,然後在執行步驟2的操作即可。

removeRange(int left, int right):如果對象裏沒有任何區間,直接返回。 否則,利用map<int, int>的lower_bound(int)函數查找第一個左區間大於或者等於left位置iter(時間複雜度是log(n)),接下來分三種情況:

  1. 如果iter == map::end,說明所有左區間都小於left(包括最後一個區間),此時a)如果最後一個區間的右區間小於等於left,直接返回即可;b)如果最後一個區間的右區間小於等於right,只需將最後一個右區間的改爲left即可;c)如果最後一個區間的右區間小於right,除了將最後一個右區間的改爲left,還有插入[right, 最後一個區間的右區間)到對象中。
  2. 如果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。
  3. iter在除以上兩種情況外的位置,此時iter前的一個區間可能與[left, right)也有交集,所以先將iter–,然後在執行步驟2的操作即可。

queryRange(int left, int right):如果對象裏沒有任何區間,返回false。否則,利用map<int, int>的lower_bound(int)函數查找第一個左區間大於或者等於left位置iter(時間複雜度是log(n)),接下來分三種情況:

  1. 如果iter == map::end,說明所有左區間都小於left(包括最後一個區間),此時只需考察最後一個區間的右區間是否大於等於right,是則返回true,否則返回false;
  2. 如果iter == map::begin,情況與1相同,只需考察第一個區間的右區間是否大於等於right,是則返回true,否則返回false;
  3. 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 ++;
                  }
              }
          }
      }
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章