一、题目描述
An integer interval [a, b]
(for integers a < b
) is a set of all consecutive integers from a
to b
, including a
and b
.
Find the minimum size of a set S such that for every integer interval A in intervals
, the intersection of S with A has size at least 2.
Example 1:
Input: intervals = [[1, 3], [1, 4], [2, 5], [3, 5]]
Output: 3
Explanation:
Consider the set S = {2, 3, 4}. For each interval, there are at least 2 elements from S in the interval.
Also, there isn’t a smaller size set that fulfills the above condition.
Thus, we output the size of this set, which is 3.
Example 2:
Input: intervals = [[1, 2], [2, 3], [2, 4], [4, 5]]
Output: 5
Explanation:
An example of a minimum sized set is {1, 2, 3, 4, 5}.
Note:
intervals
will have length in range[1, 3000]
.intervals[i]
will have length2
, representing some integer interval.intervals[i][j]
will be an integer in[0, 10^8]
.
二、题目分析
依题意,我们需要求出一个 最小 集合 S
,使得 S
中至少有两个数落在 intervals
中给出的每个闭区间中,即 S
与intervals
中给出的每个闭区间的交集的元素个数大于等于 2。最后返回该集合 S
的元素个数。
这里我们可以考虑从“最初”开始构建这样的一个集合。将 intervals
中的区间按照右端点的升序进行排序,若右端点相等,则以左端点的降序进行排序(原因说明在之后)。将排序后的一个区间的最后两个整数首先加入 S
中,作为“最初”的情况,之后再遍历处理剩下的区间。
在遍历到某一个区间 I
时,此时的 S
应能满足排在该区间之前的所有区间,在这样的前提下我们来处理这个区间 I
。简单来看,只需判断 ∣I∩S∣| I∩S |∣I∩S∣ 的大小即可。
- 若等于 2,则说明可以满足条件,无需再添加数字到
S
中。 - 若等于 1,则我们还需要从
I
中选择一个整数添加到S
中,使得它们交集的元素个数大于2,那么显然,我们选择I
中的最后一个整数,这也是我们按右端点升序排序的原因——这既能使得 ∣I∩S∣=2| I∩S | = 2∣I∩S∣=2,也能尽量地去满足之后的区间,并且S
应是最小的(S
中的数字也会按升序排序)。 - 若等于 0,则需要添加两个整数,那么我们同样选择
I
中最后的数字,但此时应是最后的两个整数,理由同上。
剩下的问题就是 ∣I∩S∣| I∩S |∣I∩S∣ 的大小了,要求交集的元素个数,又是一个痛苦的遍历过程。但在这里一个更简单的方式是,只维护 S
的最后两个元素 lastButOne
、last
。此时我们只需要判断 I
的第一个整数I[0]
和这两个数字的大小关系即可。
- 若
I[0] <= lastButOne
,即小于等于S
中倒数第二个数,那么 ∣I∩S∣| I∩S |∣I∩S∣ 应为 2,S
不会被更新。显然,此时I
会包含这两个数(因为按右端点降序排序,I
的右端点肯定会大于等于这两个包含在之前的区间中的数)。 - 若
I[0] > lastButOne && I[0] <= last
,那么 ∣I∩S∣| I∩S |∣I∩S∣ 应为 1。此时last
应含于I
。我们也要加入I
的最后一个整数到S
中,相应地更新last
和lastButOne
的值。 - 若
I[0] > last
,那么 ∣I∩S∣| I∩S |∣I∩S∣ 显然应为 0。此时加入I
的最后两个整数到S
中,相应地更新last
和lastButOne
的值。
而当右端点相等就按照左端点降序排序则是因为,这里 S
中加入的数越多,其优先度就越大,即应该更早地做(并不会影响最终结果),所以我们要使 I[0]
尽可能大,这样 I[0]
才更有机会大于 last
(这其实也避免了 last == lastButOne
的情况)。
以这种方式,我们就只需做简单的判断而避免了求交集的情况,也不用维护实际的集合 S
,大大简化了过程。
三、具体实现
按照以上分析,总体复杂度应为 O(NlogN)O(NlogN)O(NlogN),即排序的复杂度。
class Solution
{
public:
int intersectionSizeTwo( vector<vector<int>> &intervals )
{
sort( intervals.begin(), intervals.end(),
[]( const vector<int> &v1, const vector<int> &v2 )
{ return v1.back() == v2.back() ? v1.front() > v2.front() : v1.back() < v2.back(); } );
int setSize = 2;
int last = intervals.front().back(), lastButOne = last - 1;
for ( size_t i = 1; i < intervals.size(); ++i ) {
if ( intervals.at( i ).front() > lastButOne ) {
if ( intervals.at( i ).front() <= last ) {
++setSize;
lastButOne = last;
} else {
setSize += 2;
lastButOne = intervals.at( i ).back() - 1;
}
last = intervals.at( i ).back();
}
}
return setSize;
}
};