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 numbers left <= x < right.
  • 0 < left < right < 10^9 in all calls to addRange, queryRange, removeRange.
  • The total number of calls to addRange in a single test case is at most 1000.
  • The total number of calls to queryRange in a single test case is at most 5000.
  • The total number of calls to removeRange in a single test case is at most 1000.

这道题让我们实现一个RangeModule的类,里面有三个功能函数,分别好似插入范围,查找范围,删除范围,题目中的例子给的也很明确,基本不会引起什么歧义。其实不管范围也好,区间也好,都是一回事,跟之前的区间的题目Insert IntervalMerge Intervals没有什么不同。这里的插入范围函数的实现方法跟之前那道Insert Interval的解法一样,直接抄过来就好。然后对于查找范围函数,由于题目中说只要有数字未被包括,就返回false。那么我们反过来想,只有当某个范围完全覆盖了这个要查找的范围才会返回true,所以我们可以遍历所有的范围,然后看是否有一个范围完全覆盖了要查找的范围,有的话返回true,循环结束后返回false。最后来看删除范围函数,其实现方法跟插入范围函数很类似,但又有少许不同。首先我们还是新建一个数组res存结果,然后遍历已有的范围,如果当前范围的结束位置小于等于要删除的范围的起始位置,由于题目中的范围定义是左开右闭,那么说明没有重叠,加入结果res,并且用变量cur自增1来记录当前位置。如果当前范围的起始位置大于等于要删除的范围的结束位置,说明咩有重叠,加入结果res。否则就是有重叠的情况,这里跟插入范围有所不同的是,插入范围只需要加入一个范围,而删除范围操作有可能将原来的大范围break成为两个小的范围,所以我们用一个临时数组t来存break掉后的小范围。如果当前范围的起始位置小于要删除的范围的起始位置left,说明此时一定有一段范围留下来了,即从当前范围的起始位置到要删除的范围的起始位置left,将这段范围加入临时数组t,同理,如果当前范围的结束位置大于要删除的范围的结束位置right,将这段范围加入临时数组t。最后将数组t加入结果res中的cur位置即可,参见代码如下:

解法一:

class RangeModule {
public:
RangeModule() {} void addRange(int left, int right) {
vector<pair<int, int>> res;
int n = v.size(), cur = ;
for (int i = ; i < n; ++i) {
if (v[i].second < left) {
res.push_back(v[i]);
++cur;
} else if (v[i].first > right) {
res.push_back(v[i]);
} else {
left = min(left, v[i].first);
right = max(right, v[i].second);
}
}
res.insert(res.begin() + cur, {left, right});
v = res;
} bool queryRange(int left, int right) {
for (auto a : v) {
if (a.first <= left && a.second >= right) return true;
}
return false;
} void removeRange(int left, int right) {
vector<pair<int, int>> res, t;
int n = v.size(), cur = ;
for (int i = ; i < n; ++i) {
if (v[i].second <= left) {
res.push_back(v[i]);
++cur;
} else if (v[i].first >= right) {
res.push_back(v[i]);
} else {
if (v[i].first < left) {
t.push_back({v[i].first, left});
}
if (v[i].second > right) {
t.push_back({right, v[i].second});
}
}
}
res.insert(res.begin() + cur, t.begin(), t.end());
v = res;
} private:
vector<pair<int, int>> v;
};

下面来看一种优化了时间复杂度的解法,我们使用TreeMap来建立范围的起始位置和结束位置之间的映射,利用了TreeMap的自动排序功能,其会根据起始位置从小到大进行排序。既然是有序的,我们就可以利用二分法来快速进行查找了。

在加入范围函数中,我们首先用upper_bound函数来查找第一个大于left的位置,标记为l,再用upper_bound函数来查找第一个大于right的位置,标记为r。我们其实是想找第一个不大于left和right的位置的,由于C++没有floorKey这个函数,所以我们只能用upper_bound找大于left和right的位置,然后再往前移一个。如果l不是TreeMap中的第一个位置,且前面一个范围的结束位置小于left,说明和前一个范围没有交集,那么还是回到当前这个范围吧。如果此时l和r指向同一个位置,说明当前要加入的范围没有跟其他任何一个范围有交集,所以我们直接返回即可,不需要其他任何操作。否则的话我们将left和l指向范围的起始位置中的较小值赋给i,将right和r指向的前一个位置的结束位置的较大值赋给j,然后将l和r之间的范围都删除掉(注意这里r自增了1,是因为之前先自减了1),然后将i和j返回即可。返回后我们建立起这个映射即可。

在查找范围函数中,我们先用upper_bound找出第一个大于left位置的范围it,然后看如果it不是第一个范围,并且如果其前面的一个范围的结束位置大于等于right,说明已经完全包括这个要查找的范围,因为前一个范围的起始位置小于left,且结束位置大于等于right,直接返回true。

在删除范围函数中,查找重叠范围的方法跟加入范围函数中的操作一样,所以抽出来放到了find函数中,由于删除的范围有可能完全覆盖了原有范围,也有可能只是部分覆盖,将一个大的范围拆成了一个或者两个范围。所以我们判断,如果left大于覆盖范围的起始位置,那么将这段建立映射,同理,如果覆盖范围的结束位置大于right,同样建立这段的映射,参见代码如下:

解法二:

class RangeModule {
public:
RangeModule() {} void addRange(int left, int right) {
auto x = find(left, right);
m[x.first] = x.second;
} bool queryRange(int left, int right) {
auto it = m.upper_bound(left);
return it != m.begin() && (--it)->second >= right;
} void removeRange(int left, int right) {
auto x = find(left, right);
if (left > x.first) m[x.first] = left;
if (x.second > right) m[right] = x.second;
} private:
map<int, int> m; pair<int, int> find(int left, int right) {
auto l = m.upper_bound(left), r = m.upper_bound(right);
if (l != m.begin() && (--l)->second < left) ++l;
if (l == r) return {left, right};
int i = min(left, l->first), j = max(right, (--r)->second);
m.erase(l, ++r);
return {i, j};
}
};

类似题目:

Data Stream as Disjoint Intervals

Insert Interval

Merge Intervals

参考资料:

https://leetcode.com/problems/range-module/discuss/108914/c++-code

https://leetcode.com/problems/range-module/discuss/108912/C++-vector-O(n)-and-map-O(logn)-compare-two-solutions

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Range Module 范围模块的更多相关文章

  1. [Swift]LeetCode715. Range 模块 | Range Module

    A Range Module is a module that tracks ranges of numbers. Your task is to design and implement the f ...

  2. 【设计模式】module(模块)模式

    写在前面 最近刚接触到设计模式, <head first设计模式>里有一篇文章,是说使用模式的心智, 1.初学者"心智" :"我要为HELLO WORLD找个 ...

  3. 安卓与Unity交互之-Android Studio创建Module库模块教程

    安卓开发工具创建Module库 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分 ...

  4. 715. Range Module

    A Range Module is a module that tracks ranges of numbers. Your task is to design and implement the f ...

  5. AngularJS系统学习之Module(模块)

    本文源自:http://blog.csdn.net/woxueliuyun/article/details/50962645 学习之后略有所得, 来此分享.建议看原文. 模块是提供一些特殊服务的功能块 ...

  6. Range Module

    2019-09-21 18:54:16 715. Range Module 问题描述: 问题求解: 用线段树解决了. class RangeModule { Node root; class Node ...

  7. 生成器对象(自定义迭代器),自定义range方法,模块

    自定义迭代器 一 .生成器与yield ''' 我们得到一个迭代器通常都是调用可迭代对象的__iter__方法 ,例如 list.iter() 得到一个迭代器, 但是当list很大时候,就违背了pyt ...

  8. [LeetCode] Range Addition 范围相加

    Assume you have an array of length n initialized with all 0's and are given k update operations. Eac ...

  9. [LeetCode] Range Sum Query 2D - Mutable 二维区域和检索 - 可变

    Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper lef ...

随机推荐

  1. Ubuntu16.0.4下搭建pycharm 2018.3.22

    一.首先安装Java jdk Java JDK有两个版本,一个开源版本Openjdk,还有一个Oracle官方版本jdk.下面记录在Ubuntu 16.04上安装Java JDK的步骤. 安装open ...

  2. 转载:lua中switch

    刚开始使用lua的人肯定会不满lua居然没有switch这个语法. 但是熟悉lua的强大特性之后,你会发现其实switch是完全没有必要提供的^.^,因为lua有强大的table和function 例 ...

  3. 2017-2018-1 我爱学Java 第八周 作业

    团队六七周作业 团队分工 UML图 工具选择 小编(金立清)有话说 参考资料 团队分工 返回目录 UML图 用例图 类图 活动图 状态图 返回目录 工具选择 ProcessOn - 免费在线作图,实时 ...

  4. git基本用法

    基本用法(下)           一.实验说明 本节实验为 Git 入门第二个实验,继续练习最常用的git命令. 1.1 实验准备 在进行该实验之前,可以先clone一个练习项目gitproject ...

  5. 第四十四条:为所有导出的API元素编写文档注释

    简而言之,要为API编写文档,文档注释是最好,最有效的途径.对于所有可导出的API元素来说,使用文档注释应该被看作是强制性的.要 采用一致的风格来遵循标准的约定.记住,在文档注释内部出现任何的HTML ...

  6. 使用PostMan进行API自动化测试

    最近在进行一个老项目的升级,第一步是先将node版本从4.x升级到8.x,担心升级会出现问题,所以需要将服务的接口进行验证:如果手动输入各种URL,人肉check,一个两个还行,整个服务..大几十个接 ...

  7. 北亚关于HP EVA4400/6400/8400/P6000的数据恢复解决方案

    [引言]本文档建立在针对HP EVA的大量测试性研究基础上,所有的细节几乎均为对EVA的破译型研究,目前全球范围内尚未发现类似资料,故可能表述方式和结论并不精确,仅为参考之用.我们公司为研究HP EV ...

  8. List集合就这么简单【源码剖析】

    前言 声明,本文用得是jdk1.8 前一篇已经讲了Collection的总览:Collection总览,介绍了一些基础知识. 现在这篇主要讲List集合的三个子类: ArrayList 底层数据结构是 ...

  9. 《深入实践Spring Boot》阅读笔记之一:基础应用开发

    上上篇「1718总结与计划」中提到,18年要对部分项目拆分,进行服务化,并对代码进行重构.公司技术委员会也推荐使用spring boot,之前在各个技术网站中也了解过,它可以大大简化spring配置和 ...

  10. Spring Framework 的 Assert断言

    知识共享才能传播,博采众家之长,才能推陈出新!-- 参考 https://www.cnblogs.com/hwaggLee/p/4778101.html 一.什么是 Assert(断言)? Web 应 ...