LeetCode 732. My Calendar III

来源:互联网 发布:淘宝vip专享 编辑:程序博客网 时间:2024/05/16 13:42

LeetCode 732. My Calendar III

题目描述


Implement a MyCalendarThree class to store your events. A new event can always be added.

Your class will have one method, book(int start, int end). Formally, this represents a booking on the half open interval [start, end), the range of real numbers x such that start <= x < end.

A K-booking happens when K events have some non-empty intersection (ie., there is some time that is common to all K events.)

For each call to the method MyCalendar.book, return an integer K representing the largest integer such that there exists a K-booking in the calendar.

Your class will be called like this: MyCalendarThree cal = new MyCalendarThree(); MyCalendarThree.book(start, end)


题目分析

这道题有一定的背景。 现在把和问题无关的描述去掉, 大致简化为如下的问题:
每次向数轴中插入一根线段, 每次插入之后返回当前数轴上各个线段之间的最大的重合数。

这道题是在看线段树专题的时候看到的。 本来以为需要建立线段树然后balabala…… 直到看到了Discuss中的O(N),Map什么的提示,就。。发现完全不需要线段树了。
但是也可以简单把线段树的思路讲一下。
线段树放在这道题上其实非常显然, 只需要简单的调用几次线段树的方法就可以得到结果。大致如下:

  1. 每次插入一条线段, 对应更新线段树中原先线段上的值。这里,线段的值就是该线段出现的次数。
  2. 用一个变量记录最大值。每次在插入线段时维护这个最大值。 插入完之后最大值即为当次结果返回

以上就是线段树的做法。这个做法的缺点如下:
1. 线段树写起来繁琐。 这个写过的自然懂。
2. 线段树的范围太大。 注意到这道题的数据范围对朴素线段树是十分不友好的: 插入次数 < 400, 而插入范围是109 直接储存直接爆内存应该毫无疑问。 显然要做压缩。
3. 由于是动态查询, 我们不能事先知道线段的起始与终点, 因此压缩比较麻烦。。。

好吧下面开始正题。
这个题目其实也并没用到线段树的很多功能。所以尝试用直接记录起点终点来代表一个线段。
这样需要解决一点问题, 比如:
1. 若用一个数组来记录经过某个点的线段数目, 那么无法分清经过从该点的出发的还是只是经过的。
2. 若用两个数组来分别记录出发和经过,很难更新它们的值: 考虑到某个点既可能出发, 也可能是终止,还可能只是经过。。。当一个新的状态要被更新时, 我们无法从之前保存的状态完全恢复之前所有的信息。

换一种想法: 如果只是保存起始和终点的情况,而完全不管中间经过的点呢?
类似于图, 线段可以看做是一条有向边, 每次从一个点A指向B,相当于A的出度+1, B的入度 + 1。
这种情况下, 找到整个数轴上出度最大的点就是被覆盖次数最多的点。

下面是实现代码,十分简短:

class MyCalendarThree {public:    MyCalendarThree() {        maximum = 0;    }    int book(int start, int end) {        int k = 0;        ++m[start];        --m[end];        for (auto i = m.begin(); i != m.end(); ++i) {            k+= i->second;            if (maximum < k) maximum = k;        }        return maximum;    }    map<int, int> m;    int maximum;};

其他细节

  1. 这里用mapvector更加合适, 因为这里的线段的两个端点具有强离散性, 这也是线段树相比于用map不如的地方。

  2. 这里一个小trick是用了map自带的排序, 内部是用高效的方法做的。并且最后扫描的时候也十分方便。


The End.

原创粉丝点击