首先分析一下区间类问题:
- 一般都有时间顺序,
- 有开始和结束。
- 是一个vector,包含了这些开始时间和结束时间
解法总结:
- 最容易想到的就是要对时间进行排序。可能是对开始时间进行排序,也可能是对结束时间进行排序。 比如meeting room, 问一个人能不能参加所有meeting,只需要对开始时间进行排序,然后检查前面的结束,和后面的开始是不是冲突就能解决了。
- 扫描线算法,碰到一个start就+1, 碰到一个end就-1。 这个算法挺简单,可以想象成同时有多少会议在进行,开始一个就加一,结束一个就减一。 最大值就是meeting room ii 的答案,最多需要多少meeting room。
- 在排序之上,比如meeting room ii,用priority queue,对结束时间进行排序,然后就按照开始时间一个个对比。我们的目标是让pq记录在用的会议室的结束时间。 所以最后pq的size就是我们要的答案。
- 如果当前开始时间比pq上最早的结束时间要晚,那说明这两个可以共用一个会议室呀。那就pop掉这个结束时间,push 上新的结束时间。pq的长度不变。
- 如果当前开始时间小于pq上最早时间,那说明会议室不够用了,连最早结束时间都满足不了,其他肯定也不行,直接push上新的结束时间。pq长度+1
- 一直到最后一个区间,pq长度在这段时间只增加不减少。
4. 贪心算法:始终选择具有最早结束时间的区间。然后就能获得最大数量的不重叠区间。因为具有最早结束时间的区间产生了最大的容纳其余区间的能力。
- 比如当前剩余区间的最早结束时间是x。可用于其他区间的时间段就是[x:]。如果我们选择另一个结束时间为y的区间,那么可用时间段将变为[y:]。由于x ≤ y,因此没有办法使[y:]比[x:]容纳更多的区间。所以这个贪心算法可以hold。
- 因此可以按结束时间对区间进行排序,并跟踪当前最早的结束时间。一旦下一个区间的开始时间早于当前结束时间,我们就必须移除一个区间。否则,就更新最早的结束时间。
56. Merge Intervals 中等题 面试出现28次
这道题是做到现在为止出现次数最多的题了。。
也是之前入职华为的时候算法面试考的一道题的变形,真的巧了。当时考的是meeting room II, 给一堆meeting时间问需要多少会议室。正好也是谷歌高频题。
这里的做法是先对区间进行排序,然后开始遍历,记录开始和结尾。 如果新的开始在前一个结尾的前面,那就更新结尾。否则加入结果vector。
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if (intervals.size() <= 1) {
return intervals;
}
vector<vector<int>> ans;
sort(intervals.begin(), intervals.end());
int left = intervals[0][0];
int right = intervals[0][1];
for (int i = 1; i < intervals.size(); i++) {
int new_left = intervals[i][0];
int new_right = intervals[i][1];
if (new_left <= right) {
right = max(right, new_right);
} else {
ans.push_back({left, right});
left = new_left;
right = new_right;
}
}
ans.push_back({left, right});
return ans;
}
};
252 meeting room 简单题
给很多interval,问能不能参加全部会议,很简单,只需要sort之后判断有没有开始和结尾重合就好了。
因为没有leetcode 会员,所以用的lintcode。 但是lintcode 的interval是一个class,所以需要写一个lambda function,不能直接sort。
/**
* Definition of Interval:
* class Interval {
* public:
* int start, end;
* Interval(int start, int end) {
* this->start = start;
* this->end = end;
* }
* }
*/
class Solution {
public:
/**
* @param intervals: an array of meeting time intervals
* @return: if a person could attend all meetings
*/
bool canAttendMeetings(vector<Interval> &intervals) {
// Write your code here
sort(intervals.begin(), intervals.end(), [](const Interval& a, const Interval& b){return a.start < b.start;});
int newBegin;
int lastEnd = -1;
for (int i = 0; i < intervals.size(); i++) {
newBegin = intervals[i].start;
if (intervals[i].start < lastEnd) {
return false;
}
lastEnd = intervals[i].end;
}
return true;
}
};
253 meeting room II 中等题
问需要几间会议室。
min heap 解法(chatGPT)
class Solution {
public:
int minMeetingRooms(vector<vector<int>>& intervals) {
if (intervals.empty()) {
return 0;
}
// Sort the intervals based on start time
sort(intervals.begin(), intervals.end(), [](const auto& a, const auto& b) {
return a[0] < b[0];
});
// Use a min heap to keep track of end times of meetings
priority_queue<int, vector<int>, greater<int>> endTimes;
// Iterate through the intervals
for (const auto& interval : intervals) {
// If the next meeting starts after the earliest ending meeting,
// remove the earliest ending meeting (pop from min heap)
if (!endTimes.empty() && interval[0] >= endTimes.top()) {
endTimes.pop();
}
// Add the current meeting's end time to the min heap
endTimes.push(interval[1]);
}
// The size of the min heap represents the minimum number of meeting rooms required
return endTimes.size();
}
};
比较经典的扫描线的题,解法非常得简单
class Solution {
public:
/**
* @param intervals: an array of meeting time intervals
* @return: the minimum number of conference rooms required
*/
int minMeetingRooms(vector<Interval> &intervals) {
// Write your code here
vector<pair<int,int>> sweepline;
for (auto it : intervals) {
sweepline.push_back(make_pair(it.start, 1));
sweepline.push_back(make_pair(it.end, -1));
}
sort(sweepline.begin(), sweepline.end());
int room = 0;
int pre_sum = 0;
for (auto dot : sweepline) {
pre_sum += it.second;
room = max(room, pre_sum);
}
return room;
}
};
435 Nonoverlaping Intervals 中等题
给定一些interval,找出最少删除多少interval才能让剩下的都是non overlap。
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end());
int start = intervals[0][0];
int end = intervals[0][1];
int res = 0;
for (int i = 1; i < intervals.size(); i++) {
if (intervals[i][0] >= end) {
end = intervals[i][1];
continue;
} else {
end = min(intervals[i][1], end);
res++;
}
}
return res;
}
};
452. Minimum Number of Arrows to Burst Balloons 中等题
X轴上有很多气球,每个气球占一个范围[x1, x2],气球范围可以有重叠,沿着y轴射箭,问最少射多少支箭可以射穿所有的气球。
按结尾sort,这道题就变成easy题了。只要后面的开始在前面的结束之后就+1 就可以。
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
if (points.empty()) return 0;
// Sort the intervals based on their ending points
sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b) {
return a[1] < b[1];
});
int arrows = 1; // Initialize with 1 because we always need at least one arrow
int prevEnd = points[0][1]; // Initialize with the ending point of the first interval
for (int i = 1; i < points.size(); ++i) {
if (points[i][0] > prevEnd) {
// The current interval does not overlap with the previous one
// We need to use another arrow
arrows++;
prevEnd = points[i][1];
}
}
return arrows;
}
};
1288 Remove Covered Intervals 中等题
求去掉所有被cover的 interval剩下的数量,区间完全被包括了的叫做covered。
class Solution {
public:
int removeCoveredIntervals(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end());
int start = -1;
int end = -1;
int res = 0;
for (int i = 0; i < intervals.size(); i++) {
// if ==start, > end, this is a larger interval, previous one is covered.
// if >= start, <= end, this is a covered interval.
// only situation left is > start and > end
if (intervals[i][0] > start && intervals[i][1] > end) {
res++;
start = intervals[i][0];
}
end = max(intervals[i][1], end);
}
return res;
}
};