LeetCode | 1851.包含每个查询的最小区间

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。

例:第一题的图
img
当扫描线为 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值