题目大意
给出一个区间集合,合并有交集的区间并返回新集合。
解题思路
思路一
最直观的思路是使用贪心的方法,也就是按照
bool operator()(const Interval &a, const Interval &b) {
return a.start < b.start || a.start == b.start && a.end >= b.end;
}
的方法对区间进行排序。在有序的条件下,只要对区间进行一次遍历就可以完成合并了;这种方法的复杂度只有 O(nlogn)
,不过数据量很大时STL库中的 sort
函数就开始报 Runtime Error
的错,不知是我的用法有误还是其本身没法处理这么大的数据量;因为不想再写一个排序算法,所以这种思路就此作罢。
思路二
放弃了贪心的方法之后,主要思路就是在数据结构上下功夫。使用了 STL 库中的两个类 unordered_map
和 vector
:unordered_map<int, int>
用来存储区间,将 key
作为区间下界,将 value
作为区间上界;vector<int>
维护有效区间的下界(由有效的键组成的有序序列)。
这两个结构含有数据的重复,本来可以整合到一块儿,不过还是因为懒…
具体的用法是:每插入一个区间[start, end]
,便从有效区间的下界中找到最接近 start
但不大于 start
的区间起点 s
,通过比较这两个区间的关系进行插入操作,更新 unordered_map<int, int>
和 vector<int>
两个结构。
其中查找下界和更新区间的操作是最频繁的,也正是如此才额外开辟了可直接访问的 vector
结构来维护有效区间的下界,这样可以通过二分在 O(logn)
的时间内完成查找;而更新 vector<int>
则使用了插入排序,每次插入的时间复杂度为 O(n)
。最坏情况下,每次插入区间都要维护 vector<int>
,这样的时间复杂度为 O(n^2)
,要高于第一种思路;但事实上,由于区间存在许多重叠,维护的次数远小于查找操作,所以这种方法实际上也不会很慢,就是代码长了些~
源代码
Version 1 - Runtime Error
struct Comp {
bool operator()(const Interval &a, const Interval &b) {
return (a.start == b.start && a.end >= b.end) || a.start < b.start;
}
};
class Solution {
public:
vector<Interval> merge(vector<Interval>& intervals) {
if (intervals.empty()) return intervals;
Comp comp;
sort(intervals.begin(), intervals.end(), comp);
vector<Interval> res;
res.push_back(intervals[0]);
for (int i = 1; i < intervals.size(); i++) {
if (intervals[i].end <= res.back().end) continue;
else {
if (intervals[i].start <= res.back().end) {
res.back().end = intervals[i].end;
}
else {
res.push_back(Interval(intervals[i].start, intervals[i].end));
}
}
}
return res;
}
};
Version 2
/**
* Definition for an interval.
* struct Interval {
* int start;
* int end;
* Interval() : start(0), end(0) {}
* Interval(int s, int e) : start(s), end(e) {}
* };
*/
class Intervals {
public:
Intervals(int start, int end) {
starts.push_back(start);
mapping[start] = end;
}
void insert(int start, int end) {
int index = find(start);
if (index >= 0) {
int s = starts[index];
if (mapping[s] >= end) return;
else if (start <= mapping[s]) {
mapping[s] = end;
merge(index);
}
else {
mapping[start] = end;
starts.push_back(0);
for (int i = starts.size() - 1; i > index + 1; i--) {
starts[i] = starts[i - 1];
}
starts[index + 1] = start;
merge(index + 1);
}
}
else {
mapping[start] = end;
starts.push_back(0);
for (int i = starts.size(); i > 0; i--) {
starts[i] = starts[i - 1];
}
starts[0] = start;
merge(0);
}
}
int find(int s) {
if (s < starts[0]) return -1;
int left, right, mid;
left = 0; right = starts.size() - 1;
while (left < right) {
mid = (left + right) / 2;
if (starts[mid] == s) return mid;
else if (starts[mid] > s) {
right = mid - 1;
}
else {
if (starts[mid + 1] <= s) {
left = mid + 1;
}
else return mid;
}
}
return left;
}
void merge(int index) {
int target = starts[index];
while (index + 1 < starts.size() && mapping[target] >= starts[index + 1]) {
mapping[target] = mapping[target] >= mapping[starts[index + 1]]? mapping[target] : mapping[starts[index + 1]];
for (int i = index + 1; i < starts.size(); i++) {
starts[i] = starts[i + 1];
}
starts.pop_back();
}
}
void print(vector<Interval> &res) {
for (int i = 0; i < starts.size(); i++) {
res.push_back(Interval(starts[i], mapping[starts[i]]));
}
}
private:
unordered_map<int, int> mapping;
vector<int> starts;
};
class Solution {
public:
vector<Interval> merge(vector<Interval>& intervals) {
if (intervals.empty()) return intervals;
Intervals my_intervals(intervals[0].start, intervals[0].end);
for (int i = 1; i < intervals.size(); i++) {
my_intervals.insert(intervals[i].start, intervals[i].end);
}
vector<Interval> res;
my_intervals.print(res);
return res;
}
};