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
  • intervals[i].length == 2
  • 1 <= lefti <= righti <= 107
  • 1 <= queries[j] <= 107

来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。力扣

 题解:离线算法+优先队列

如果对优先队列还不了解,可以先查看:C++ 优先队列 priority_queue 使用篇_爱喝酸奶!的博客-CSDN博客介绍了学习priority_queue应该具有的储备知识:仿函数。通过对类的模板参数和函数的参数列表的解读来为后续的学习做铺垫。通过大量的代码示例来讲解三种情境下的元素存储,步步深入,学会仿函数和priority_queue的使用。_priority_queuehttps://blog.csdn.net/weixin_57761086/article/details/126802156

如果 queries 询问是在线的,即询问是一个一个到来的,必须回答前一个询问后才能回答下一个,那么对于每个询问,我们的操作是:

1.清空优先队列,将所有左端点小于等于该 Query 值的区间加入优先队列,该队列采取小根堆的形式,比较值为区间的长度,即区间长度最小的位于队首;

2.判断队首区间的右端点是否大于等于该 Query 值,若小于则弹出,直到队列为空或得到满足的区间;

3.若队列不为空,则返回队首区间的长度作为答案,若为空,则返回 -1 。

回到当前题目,我们已经得到了所有的 queries ,即是离线的,那么我们能否思考一种更优化的算法,使得每次更新优先队列的时候能在前一个状态的基础上进行操作,而不用每次都进行清空操作。

如果区间按左端点的值升序排序,且询问的值是递增的,那么我们可以用一个指针来标记未加入优先队列的最近的区间。

当上一个询问完成时,因为询问值是递增的,所以当前优先队列中的所有区间的左端点一定是小于等于下一个询问值的。

因此,我们只需要从指针的位置开始,将满足条件的所有区间加入优先队列,然后再进行队首判断操作即可,这样我们只需要遍历一遍区间数组就可以完成所有询问。

 代码:

class Solution {
public:
    vector<int> minInterval(vector<vector<int>>& intervals, vector<int>& queries) {
        vector<int> qindex(queries.size());
        for(int i=0;i<=qindex.size()-1;i++)
        {
            qindex[i]=i;
        }
        // iota(qindex.begin(), qindex.end(), 0);  上述for循环的等价语句
        sort(qindex.begin(),qindex.end(),[&](int i, int j) -> bool {
            return queries[i] < queries[j];
        });
        sort(intervals.begin(), intervals.end(), [](const vector<int> &it1, const vector<int> &it2) -> bool {
            return it1[0] < it2[0];
        });
        priority_queue<vector<int>, vector<vector<int>>, greater<vector<int>>> pq;
        vector<int> ans(queries.size(),-1);
        int now = 0;
        for(int i=0;i<=qindex.size()-1;i++)
        {
            while(now<intervals.size()&&intervals[now][0]<=queries[qindex[i]])
            {
                pq.push({intervals[now][1]-intervals[now][0]+1,intervals[now][0],intervals[now][1]});
                now++;
            }
            while(!pq.empty()&&pq.top()[2]<queries[qindex[i]])
            {
                pq.pop();
            }
            if(!pq.empty()) ans[qindex[i]]=pq.top()[0];
        }
        return ans;
    }
};

这里 qindex 的作用时记录每个询问值在原询问值数组中的位置,以此来保证输出答案时是按原顺序输出的。

vector<int> qindex(queries.size());

用 greater 参数可以规定优先队列的排序方式为小根堆,优先队列默认采取的方式是大根堆

priority_queue<vector<int>, vector<vector<int>>, greater<vector<int>>> pq;

复杂度分析:

  • 时间复杂度:O(mlog⁡m+nlog⁡n),其中 m 和 n 分别为 intervals 和 queries 的长度。排序需要 O(mlogm+nlogn),最多执行 m 次入队和出队操作,需要 O(mlogm)。
  • 空间复杂度:O(m+n)。保存 qindex 需要 O(m),保存 pq 需要 O(n)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值