LeetCode | 1851.包含每个查询的最小区间
给你一个二维整数数组 intervals ,其中 intervals[i] = [lefti, righti] 表示第 i 个区间开始于 lefti 、结束于 righti(包含两侧取值,闭区间)。区间的 长度 定义为区间中包含的整数数目,更正式地表达是 righti - lefti + 1 。
再给你一个整数数组 queries 。第 j 个查询的答案是满足 lefti <= queries[j] <= righti 的 长度最小区间 i 的长度。如果不存在这样的区间,那么答案是 -1 。
以数组形式返回对应查询的所有答案。
示例 1:
输入:intervals = [[1,4],[2,4],[3,6],[4,4]], queries = [2,3,4,5]
输出:[3,3,1,4]
解释:查询处理如下:
- Query = 2 :区间 [2,4] 是包含 2 的最小区间,答案为 4 - 2 + 1 = 3 。
- Query = 3 :区间 [2,4] 是包含 3 的最小区间,答案为 4 - 2 + 1 = 3 。
- Query = 4 :区间 [4,4] 是包含 4 的最小区间,答案为 4 - 4 + 1 = 1 。
- Query = 5 :区间 [3,6] 是包含 5 的最小区间,答案为 6 - 3 + 1 = 4 。
示例 2:
输入:intervals = [[2,3],[2,5],[1,8],[20,25]], queries = [2,19,5,22]
输出:[2,-1,4,6]
解释:查询处理如下:
- Query = 2 :区间 [2,3] 是包含 2 的最小区间,答案为 3 - 2 + 1 = 2 。
- Query = 19:不存在包含 19 的区间,答案为 -1 。
- Query = 5 :区间 [2,5] 是包含 5 的最小区间,答案为 5 - 2 + 1 = 4 。
- Query = 22:区间 [20,25] 是包含 22 的最小区间,答案为 25 - 20 + 1 = 6 。
提示:
- 1 <= intervals.length <= 105
- 1 <= queries.length <= 105
- queries[i].length == 2
- 1 <= lefti <= righti <= 107
- 1 <= queries[j] <= 107
思路:扫描线加优先队列,和 LeetCode | 218. 天际线问题 类似。
把 intervals[i] 的左右两个边界当成是矩形的两条竖边的 x 坐标,矩形的高度为 righti - lefti + 1; queries[j] 就是扫描线,查询的结果就是 queries[j] 所在位置的高度最低的矩形,如果没有矩形,返回 -1。
例:第一题的图
当扫描线为 2 时,最低的矩形是 3;扫面线为 4 时,最低的矩形是 1(因为区间为[4, 4], 所以只能看见一根红色的线)。
实现一:把要查询的区间和扫描线放到同一个数据结构中保存,从左往右遍历
struct Event {
int type;
int pos;
int para;
Event(int _type, int _pos, int para): type(_type), pos(_pos), para(para) {}
bool operator< (const Event& that) const {
return pos < that.pos || (pos == that.pos && type < that.type);
}
};
vector<int> minInterval(vector<vector<int>>& intervals, vector<int>& queries) {
int n = intervals.size();
int m = queries.size();
vector<Event> events;
for (int i = 0; i < m; ++i) {
events.emplace_back(1, queries[i], i);
}
for (const auto& interval: intervals) {
events.emplace_back(0, interval[0], interval[1]);
events.emplace_back(2, interval[1], interval[0]);
}
sort(events.begin(), events.end());
vector<int> ans(m, -1);
multiset<int> seg;
for (const auto& event: events) {
if (event.type == 0) {
seg.insert(event.para - event.pos + 1);
} else if (event.type == 1) {
if (!seg.empty()) {
ans[event.para] = *seg.begin();
}
} else {
int len = event.pos - event.para + 1;
auto it = seg.find(len);
seg.erase(it);
}
}
return ans;
}
实现二: 用优先队列保存扫描线所在的区间,扫描线不在区间时,区间移出队列。
vector<int> minInterval(vector<vector<int>>& intervals, vector<int>& queries) {
vector<int> queries_B = queries, res;
sort(intervals.begin(), intervals.end());
sort(queries_B.begin(), queries_B.end());
auto cmp = [](const pair<int, int>& a, const pair<int, int>& b) -> bool { return a.first > b.first; };
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> que(cmp);
int n = intervals.size(), idx = 0;
map<int, int> tmp;
for (int it : queries_B) {
while (idx < n && intervals[idx][0] <= it) {
que.emplace(intervals[idx][1] - intervals[idx][0] + 1, intervals[idx][1]);
idx++;
}
while (!que.empty() && que.top().second < it) {
que.pop();
}
tmp[it] = que.empty() ? -1 : que.top().first;
}
for (int it : queries) {
res.push_back(tmp[it]);
}
return res;
}