1. 静态区间合并
先按左边界排序,再两两比较合并
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if (intervals.size() == 0) return {};
sort(intervals.begin(), intervals.end());//按左边界排序
vector<vector<int>> merged;//需要空间存储合并结果
for (int i = 0; i < intervals.size(); ++i) {
int L = intervals[i][0], R = intervals[i][1];
//如果存储空间没有元素,或末元素右边界小于新元素左边界,加入区间
if (!merged.size() || merged.back()[1] < L)
merged.push_back({L, R});
//否则进行合并,右边界更新为两合并元素较大值
else merged.back()[1] = max(merged.back()[1], R);
}
return merged;
}
};
2. 我的日程安排表I
如果可以将日程(区间)安排成功添加到日历(区间集合)中而不会导致重复预订(存在交集)
返回 true ,否则,返回 false 并且不要将该日程安排添加到日历中
暴力法
将新加入区间,与已有区间逐个比较,判断有无交集即可
暴力法
vector<pair<int, int>> vec;
bool book(int start, int end) {
for (auto& [x, y] : vec)//遍历已存储所有的区间
if (x < end && y > start) return false;//判断新加入区间是否有交集
vec.emplace_back(pair{start, end});//存储新的区间
return true;
}
暴力法优化(二分)
使用map存储区间,利用其有序的结构特点,进行二分查找并比较
map+二分
class MyCalendar {
public:
map<int, int> pool;
MyCalendar() {}
bool book(int start, int end) {
// lower_bound()返回值是一个迭代器,返回指向左边界>=end的第一个值的位置
map<int, int>::iterator it = pool.lower_bound(end);//二分查找需要比较的位置,是得到位置的前一个位置
// 判断是否为第一个元素,或前一个元素的结束时间<= start
if (it == pool.begin() || (--it)->second <= start) {//右边界在所有区间左侧或者左边界在前一个元素右边界之后
pool[start] = end;//插入该区间
return true;
}
return false;
}
};
插旗法
通过插旗计数来验证左边界前的区间是否闭合,以及是否有位置落在新加入区间之间
使用map结构自动排序
插旗法
class MyCalendar {
public:
MyCalendar() {}
bool book(int start, int end) {
int ans = 0;
int left = 0;//从前往后计数值
for (auto &[pos, freq] : cnt) {
if(pos<=start) left += freq; //计前缀和
else if(pos<end) return false;//说明start和end间必然存在其他区间
}
if(left==0){ //如果计数为0说明前面区间皆已闭合
//插入新区间
cnt[start]++;//起点加一
cnt[end]--;//终点减一
return true;}
else return false;
}
private:
map<int, int> cnt;//这里使用map自动排序
};
3. 我的日程安排表II(插旗法)
如果要添加的时间内不会导致三重预订时,则可以存储这个新的日程安排
这里我们不用去判断,直接尝试加入,看累加值是否会等于3即可,注意插入失败即时撤回
bool book(int start, int end) {
int ans = 0;
cnt[start]++;//起点加一,先尝试加入
cnt[end]--;//终点减一
for (auto &[pos, freq] : cnt) {
ans += freq; //从前往后累加
if(ans==3){cnt[start]--;cnt[end]++;return false;}//撤回并返回插入失败
}
return true;
}
4. 我的日程安排表III(插旗法)
从前往后求差分数组前缀和的时候,记录一个最大值即可
int book(int start, int end) {
int ans = 0;
int maxBook = 0;
cnt[start]++;//起点加一
cnt[end]--;//终点减一
for (auto &[_, freq] : cnt) {
maxBook += freq; //从前往后累加
ans = max(maxBook, ans); //记录最大值
}
return ans;
}